https://github.com/tbaederr created
https://github.com/llvm/llvm-project/pull/202260
Similar to what we do in GetPtrField{,Pop}.
>From afda16d9de1f94c170ac667b1a85f010d2dfa28e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Mon, 8 Jun 2026 07:52:30 +0200
Subject: [PATCH] [clang][bytecode] Reject GetField{,Pop} ops on non-records
Similar to what we do in GetPtrField{,Pop}.
---
clang/lib/AST/ByteCode/Interp.h | 38 +++++---
.../ByteCode/libcxx/getfield-nonrecord.cpp | 89 +++++++++++++++++++
2 files changed, 115 insertions(+), 12 deletions(-)
create mode 100644 clang/test/AST/ByteCode/libcxx/getfield-nonrecord.cpp
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index fa77e19afce66..0be4dff0a9438 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1631,6 +1631,13 @@ bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
return false;
if (!CheckRange(S, OpPC, Obj, CSK_Field))
return false;
+
+ // FIXME(postswitch): The isUnknownSizeArray() check here is only needed
+ // to keep an invalid sample producing the same diagnostics as the current
+ // interpreter.
+ if (!Obj.getFieldDesc()->isRecord() && !Obj.isUnknownSizeArray())
+ return false;
+
const Pointer &Field = Obj.atField(I);
if (!CheckLoad(S, OpPC, Field))
return false;
@@ -1638,35 +1645,42 @@ bool GetField(InterpState &S, CodePtr OpPC, uint32_t I)
{
return true;
}
+/// 1) Pops a pointer from the stack
+/// 2) Pushes the value of the pointer's field on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
- const T &Value = S.Stk.pop<T>();
- const Pointer &Obj = S.Stk.peek<Pointer>();
+bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const Pointer &Obj = S.Stk.pop<Pointer>();
if (!CheckNull(S, OpPC, Obj, CSK_Field))
return false;
if (!CheckRange(S, OpPC, Obj, CSK_Field))
return false;
+
+ // FIXME(postswitch): The isUnknownSizeArray() check here is only needed
+ // to keep an invalid sample producing the same diagnostics as the current
+ // interpreter.
+ if (!Obj.getFieldDesc()->isRecord() && !Obj.isUnknownSizeArray())
+ return false;
+
const Pointer &Field = Obj.atField(I);
- if (!CheckStore(S, OpPC, Field))
+ if (!CheckLoad(S, OpPC, Field))
return false;
- Field.initialize();
- Field.deref<T>() = Value;
+ S.Stk.push<T>(Field.deref<T>());
return true;
}
-/// 1) Pops a pointer from the stack
-/// 2) Pushes the value of the pointer's field on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
- const Pointer &Obj = S.Stk.pop<Pointer>();
+bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Obj = S.Stk.peek<Pointer>();
if (!CheckNull(S, OpPC, Obj, CSK_Field))
return false;
if (!CheckRange(S, OpPC, Obj, CSK_Field))
return false;
const Pointer &Field = Obj.atField(I);
- if (!CheckLoad(S, OpPC, Field))
+ if (!CheckStore(S, OpPC, Field))
return false;
- S.Stk.push<T>(Field.deref<T>());
+ Field.initialize();
+ Field.deref<T>() = Value;
return true;
}
diff --git a/clang/test/AST/ByteCode/libcxx/getfield-nonrecord.cpp
b/clang/test/AST/ByteCode/libcxx/getfield-nonrecord.cpp
new file mode 100644
index 0000000000000..e4b4463445a83
--- /dev/null
+++ b/clang/test/AST/ByteCode/libcxx/getfield-nonrecord.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -std=c++2c -fexperimental-new-constant-interpreter
-verify=expected,both %s -Wno-undefined-internal
+// RUN: %clang_cc1 -std=c++2c -verify=ref,both
%s -Wno-undefined-internal
+
+// both-no-diagnostics
+
+namespace std {
+inline namespace {
+template <class _Tp> constexpr _Tp &&forward(_Tp &);
+template <class _Tp> constexpr __remove_reference_t(_Tp) &&move(_Tp &&);
+template <decltype(sizeof(int)), class> struct tuple_element;
+template <class> struct tuple_size;
+template <class, class _T2> struct pair {
+ _T2 second;
+};
+template <class _T1, class _T2> pair<_T1, _T2> make_pair(_T1, _T2);
+template <class _T1, class _T2> struct tuple_size<pair<_T1, _T2>> {
+ static const int value = 2;
+};
+template <class _T1, class _T2> struct tuple_element<0, pair<_T1, _T2>> {
+ using type = _T1;
+};
+template <class _T1, class _T2> struct tuple_element<1, pair<_T1, _T2>> {
+ using type = _T2;
+};
+template <int> struct __get_pair {
+ template <class _T1, class _T2> static _T1 &&get(pair<_T1, _T2>);
+};
+template <> struct __get_pair<1> {
+ template <class _T1, class _T2>
+ static constexpr _T2 &&get(pair<_T1, _T2> &&__p) {
+ return std::forward(__p.second);
+ }
+};
+template <int _Ip, class _T1, class _T2>
+constexpr tuple_element<_Ip, pair<_T1, _T2>>::type &&get(pair<_T1, _T2> &&__p)
{
+ return __get_pair<_Ip>::get(std::move(__p));
+}
+namespace _Algorithm {
+struct __for_each;
+}
+template <class> struct __single_range;
+template <class...> struct __specialized_algorithm;
+template <class, class> using for_each_result = int;
+struct {
+ template <typename _Range, class _Func>
+ for_each_result<_Range, _Func> operator()(_Range __range, _Func __func) {
+ using _SpecialAlg =
+ __specialized_algorithm<_Algorithm::__for_each,
__single_range<_Range>>;
+ _SpecialAlg()(__range, __func, 0);
+ return {};
+ }
+} for_each;
+template <class, class, class> struct __tree;
+template <class _Tp, class _Compare, class _Allocator>
+struct __specialized_algorithm<
+ _Algorithm::__for_each, __single_range<__tree<_Tp, _Compare, _Allocator>>>
{
+ template <class _Tree, class _Func, class _Proj>
+ auto operator()(_Tree, _Func __func, _Proj) {
+ return make_pair(0, __func);
+ }
+};
+template <class, class, class = int, class = int> struct map {
+ typedef __tree<int, int, int> __base;
+};
+template <class _Key, class _Tp, class _Compare, class _Allocator>
+struct __specialized_algorithm<
+ _Algorithm::__for_each,
+ __single_range<map<_Key, _Tp, _Compare, _Allocator>>> {
+ template <class _Map, class _Func, class _Proj>
+ auto operator()(_Map __map, _Func __func, _Proj) {
+ auto [_, __func2] = __specialized_algorithm<
+ _Algorithm::__for_each,
+ __single_range<typename map<_Compare, _Allocator>::__base>>()(
+ __map, __func, 0);
+ return __func2;
+ }
+};
+} // namespace
+} // namespace std
+
+
+template <class Converter> void test_node_container(Converter) {
+ std::map<int, int> c;
+ int invoke_count;
+ std::for_each(c, [&invoke_count] {});
+}
+void test() {
+ test_node_container([] {});
+}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits