From aea7467de08783ab372d1645358934c350ddeca3 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Thu, 9 Jul 2026 21:57:39 +0300
Subject: [PATCH 1/7] Temporary fix
Signed-off-by: Petr Shumilov
---
runtime-common/core/core-context.h | 12 ++++
.../core/core-types/definition/mixed.inl | 70 ++++++++++++++++---
2 files changed, 73 insertions(+), 9 deletions(-)
diff --git a/runtime-common/core/core-context.h b/runtime-common/core/core-context.h
index 30137813fd..71345a7f5c 100644
--- a/runtime-common/core/core-context.h
+++ b/runtime-common/core/core-context.h
@@ -6,8 +6,11 @@
#include
#include
+#include
#include "common/mixin/not_copyable.h"
+#include "runtime-common/core/allocator/script-allocator.h"
+#include "runtime-common/core/std/containers.h"
#ifndef INCLUDED_FROM_KPHP_CORE
#error "this file must be included only from runtime-core.h"
@@ -17,6 +20,15 @@ struct RuntimeContext final : vk::not_copyable {
int32_t show_migration_php8_warning{};
int32_t php_disable_warnings{};
uint32_t empty_obj_count{};
+ struct {
+ kphp::stl::unordered_map objects;
+ void* bool_v{};
+ void* int64_v{};
+ void* double_v{};
+ void* string_v{};
+ void* mixed_v{};
+ void* array_v{};
+ } empty_value;
string_buffer_lib_context sb_lib_context{};
string_buffer static_SB{};
diff --git a/runtime-common/core/core-types/definition/mixed.inl b/runtime-common/core/core-types/definition/mixed.inl
index d291a7725c..e5c59a88ce 100644
--- a/runtime-common/core/core-types/definition/mixed.inl
+++ b/runtime-common/core/core-types/definition/mixed.inl
@@ -8,7 +8,11 @@
#include "common/smart_ptrs/intrusive_ptr.h"
#include "runtime-common/core/class-instance/refcountable-php-classes.h"
+#include "runtime-common/core/runtime-core.h"
#include "runtime-common/core/utils/migration-php8.h"
+#include
+#include
+#include
#ifndef INCLUDED_FROM_KPHP_CORE
#error "this file must be included only from runtime-core.h"
@@ -173,22 +177,70 @@ inline void swap(mixed& lhs, mixed& rhs) {
lhs.swap(rhs);
}
+template
+struct dependent_false : std::false_type {};
+
+template
+inline constexpr bool dependent_false_v = dependent_false::value;
+
template
T& mixed::empty_value() noexcept {
static_assert(vk::is_type_in_list>{} || is_type_acceptable_for_mixed::value, "unsupported type");
- static T value;
- value = T{};
- return value;
+ auto& ctx{RuntimeContext::get()};
+ if constexpr (is_type_acceptable_for_mixed::value) {
+ auto& type2value{ctx.empty_value.objects};
+ const auto it{type2value.find(std::type_index(typeid(T)))};
+ if (it != type2value.end()) {
+ return *reinterpret_cast(it->second);
+ }
+ auto* obj{new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{}};
+ type2value.insert({std::type_index(typeid(T)), static_cast(obj)});
+ return *obj;
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.bool_v == nullptr) {
+ ctx.empty_value.bool_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ return *reinterpret_cast(ctx.empty_value.bool_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.int64_v == nullptr) {
+ ctx.empty_value.int64_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ return *reinterpret_cast(ctx.empty_value.int64_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.double_v == nullptr) {
+ ctx.empty_value.double_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ return *reinterpret_cast(ctx.empty_value.double_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.string_v == nullptr) {
+ ctx.empty_value.string_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ return *reinterpret_cast(ctx.empty_value.string_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.mixed_v == nullptr) {
+ ctx.empty_value.mixed_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ return *reinterpret_cast(ctx.empty_value.mixed_v);
+ } else if constexpr (std::is_same_v>) {
+ if (ctx.empty_value.array_v == nullptr) {
+ ctx.empty_value.array_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ return *reinterpret_cast(ctx.empty_value.array_v);
+ } else {
+ static_assert(dependent_false_v, "Unsupported type provided!");
+ }
}
inline void mixed::reset_empty_values() noexcept {
- empty_value();
- empty_value();
- empty_value();
- empty_value();
- empty_value();
- empty_value>();
+ auto& ctx{RuntimeContext::get()};
+ ctx.empty_value.objects.clear();
+ ctx.empty_value.bool_v = nullptr;
+ ctx.empty_value.int64_v = nullptr;
+ ctx.empty_value.double_v = nullptr;
+ ctx.empty_value.string_v = nullptr;
+ ctx.empty_value.mixed_v = nullptr;
+ ctx.empty_value.array_v = nullptr;
}
template
From a58cd3151da0619c4e362429089939a51e5c2de1 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Fri, 10 Jul 2026 19:19:57 +0300
Subject: [PATCH 2/7] Adjust semantics
Signed-off-by: Petr Shumilov
---
runtime-common/core/core-context.h | 13 +--
.../core/core-types/definition/mixed.inl | 83 ++++++-------------
2 files changed, 26 insertions(+), 70 deletions(-)
diff --git a/runtime-common/core/core-context.h b/runtime-common/core/core-context.h
index 71345a7f5c..5baab45526 100644
--- a/runtime-common/core/core-context.h
+++ b/runtime-common/core/core-context.h
@@ -6,11 +6,8 @@
#include
#include
-#include
#include "common/mixin/not_copyable.h"
-#include "runtime-common/core/allocator/script-allocator.h"
-#include "runtime-common/core/std/containers.h"
#ifndef INCLUDED_FROM_KPHP_CORE
#error "this file must be included only from runtime-core.h"
@@ -20,15 +17,7 @@ struct RuntimeContext final : vk::not_copyable {
int32_t show_migration_php8_warning{};
int32_t php_disable_warnings{};
uint32_t empty_obj_count{};
- struct {
- kphp::stl::unordered_map objects;
- void* bool_v{};
- void* int64_v{};
- void* double_v{};
- void* string_v{};
- void* mixed_v{};
- void* array_v{};
- } empty_value;
+ void* empty_values{nullptr};
string_buffer_lib_context sb_lib_context{};
string_buffer static_SB{};
diff --git a/runtime-common/core/core-types/definition/mixed.inl b/runtime-common/core/core-types/definition/mixed.inl
index e5c59a88ce..4de7129a27 100644
--- a/runtime-common/core/core-types/definition/mixed.inl
+++ b/runtime-common/core/core-types/definition/mixed.inl
@@ -4,15 +4,18 @@
#pragma once
+#include
+#include
+
#include "common/algorithms/find.h"
#include "common/smart_ptrs/intrusive_ptr.h"
+#include "runtime-common/core/allocator/script-allocator.h"
#include "runtime-common/core/class-instance/refcountable-php-classes.h"
#include "runtime-common/core/runtime-core.h"
+#include "runtime-common/core/std/containers.h"
+#include "runtime-common/core/utils/kphp-assert-core.h"
#include "runtime-common/core/utils/migration-php8.h"
-#include
-#include
-#include
#ifndef INCLUDED_FROM_KPHP_CORE
#error "this file must be included only from runtime-core.h"
@@ -177,70 +180,34 @@ inline void swap(mixed& lhs, mixed& rhs) {
lhs.swap(rhs);
}
-template
-struct dependent_false : std::false_type {};
-
-template
-inline constexpr bool dependent_false_v = dependent_false::value;
-
template
T& mixed::empty_value() noexcept {
static_assert(vk::is_type_in_list>{} || is_type_acceptable_for_mixed::value, "unsupported type");
auto& ctx{RuntimeContext::get()};
- if constexpr (is_type_acceptable_for_mixed::value) {
- auto& type2value{ctx.empty_value.objects};
- const auto it{type2value.find(std::type_index(typeid(T)))};
- if (it != type2value.end()) {
- return *reinterpret_cast(it->second);
- }
- auto* obj{new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{}};
- type2value.insert({std::type_index(typeid(T)), static_cast(obj)});
- return *obj;
- } else if constexpr (std::is_same_v) {
- if (ctx.empty_value.bool_v == nullptr) {
- ctx.empty_value.bool_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- return *reinterpret_cast(ctx.empty_value.bool_v);
- } else if constexpr (std::is_same_v) {
- if (ctx.empty_value.int64_v == nullptr) {
- ctx.empty_value.int64_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- return *reinterpret_cast(ctx.empty_value.int64_v);
- } else if constexpr (std::is_same_v) {
- if (ctx.empty_value.double_v == nullptr) {
- ctx.empty_value.double_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- return *reinterpret_cast(ctx.empty_value.double_v);
- } else if constexpr (std::is_same_v) {
- if (ctx.empty_value.string_v == nullptr) {
- ctx.empty_value.string_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- return *reinterpret_cast(ctx.empty_value.string_v);
- } else if constexpr (std::is_same_v) {
- if (ctx.empty_value.mixed_v == nullptr) {
- ctx.empty_value.mixed_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- return *reinterpret_cast(ctx.empty_value.mixed_v);
- } else if constexpr (std::is_same_v>) {
- if (ctx.empty_value.array_v == nullptr) {
- ctx.empty_value.array_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- return *reinterpret_cast(ctx.empty_value.array_v);
- } else {
- static_assert(dependent_false_v, "Unsupported type provided!");
+
+ using type2value_t = kphp::stl::unordered_map;
+ if (ctx.empty_values == nullptr) {
+ auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(type2value_t))};
+ php_assert(raw_mem);
+ ctx.empty_values = new (raw_mem) type2value_t{};
+ reinterpret_cast(ctx.empty_values)->reserve(6); // bool, int64_t, double, string, mixed, array
+ }
+
+ auto* type2value{reinterpret_cast(ctx.empty_values)};
+ const auto it{type2value->find(std::type_index(typeid(T)))};
+ if (it != type2value->end()) {
+ *reinterpret_cast(it->second) = T{};
+ return *reinterpret_cast(it->second);
}
+
+ auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
+ php_assert(raw_mem);
+ return *reinterpret_cast(type2value->insert({std::type_index(typeid(T)), new (raw_mem) T{}}).first->second);
}
inline void mixed::reset_empty_values() noexcept {
- auto& ctx{RuntimeContext::get()};
- ctx.empty_value.objects.clear();
- ctx.empty_value.bool_v = nullptr;
- ctx.empty_value.int64_v = nullptr;
- ctx.empty_value.double_v = nullptr;
- ctx.empty_value.string_v = nullptr;
- ctx.empty_value.mixed_v = nullptr;
- ctx.empty_value.array_v = nullptr;
+ RuntimeContext::get().empty_values = nullptr;
}
template
From 6fcf4a90aba797a6982c24811d625c0b039006e5 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Mon, 13 Jul 2026 16:15:54 +0300
Subject: [PATCH 3/7] Improve performance
Signed-off-by: Petr Shumilov
---
common/type_traits/dependent_false.h | 17 ++++
runtime-common/core/core-context.h | 10 ++-
.../core/core-types/definition/mixed.inl | 81 +++++++++++++++----
3 files changed, 90 insertions(+), 18 deletions(-)
create mode 100644 common/type_traits/dependent_false.h
diff --git a/common/type_traits/dependent_false.h b/common/type_traits/dependent_false.h
new file mode 100644
index 0000000000..00174a5934
--- /dev/null
+++ b/common/type_traits/dependent_false.h
@@ -0,0 +1,17 @@
+// Compiler for PHP (aka KPHP)
+// Copyright (c) 2020 LLC «V Kontakte»
+// Distributed under the GPL v3 License, see LICENSE.notice.txt
+
+#pragma once
+
+#include
+
+namespace vk {
+
+template
+struct dependent_false : std::false_type {};
+
+template
+inline constexpr bool dependent_false_v = dependent_false::value;
+
+} // namespace vk
diff --git a/runtime-common/core/core-context.h b/runtime-common/core/core-context.h
index 5baab45526..8e40f9b683 100644
--- a/runtime-common/core/core-context.h
+++ b/runtime-common/core/core-context.h
@@ -17,7 +17,15 @@ struct RuntimeContext final : vk::not_copyable {
int32_t show_migration_php8_warning{};
int32_t php_disable_warnings{};
uint32_t empty_obj_count{};
- void* empty_values{nullptr};
+ struct {
+ void* bool_v{};
+ void* int64_v{};
+ void* double_v{};
+ void* string_v{};
+ void* mixed_v{};
+ void* array_v{};
+ void* objects{};
+ } empty_value;
string_buffer_lib_context sb_lib_context{};
string_buffer static_SB{};
diff --git a/runtime-common/core/core-types/definition/mixed.inl b/runtime-common/core/core-types/definition/mixed.inl
index 4de7129a27..3665bd548c 100644
--- a/runtime-common/core/core-types/definition/mixed.inl
+++ b/runtime-common/core/core-types/definition/mixed.inl
@@ -9,6 +9,7 @@
#include "common/algorithms/find.h"
#include "common/smart_ptrs/intrusive_ptr.h"
+#include "common/type_traits/dependent_false.h"
#include "runtime-common/core/allocator/script-allocator.h"
#include "runtime-common/core/class-instance/refcountable-php-classes.h"
@@ -185,29 +186,75 @@ T& mixed::empty_value() noexcept {
static_assert(vk::is_type_in_list>{} || is_type_acceptable_for_mixed::value, "unsupported type");
auto& ctx{RuntimeContext::get()};
+ if constexpr (std::is_same_v) {
+ if (ctx.empty_value.bool_v == nullptr) {
+ ctx.empty_value.bool_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ *reinterpret_cast(ctx.empty_value.bool_v) = T{};
+ return *reinterpret_cast(ctx.empty_value.bool_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.int64_v == nullptr) {
+ ctx.empty_value.int64_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ *reinterpret_cast(ctx.empty_value.int64_v) = T{};
+ return *reinterpret_cast(ctx.empty_value.int64_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.double_v == nullptr) {
+ ctx.empty_value.double_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ *reinterpret_cast(ctx.empty_value.double_v) = T{};
+ return *reinterpret_cast(ctx.empty_value.double_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.string_v == nullptr) {
+ ctx.empty_value.string_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ *reinterpret_cast(ctx.empty_value.string_v) = T{};
+ return *reinterpret_cast(ctx.empty_value.string_v);
+ } else if constexpr (std::is_same_v) {
+ if (ctx.empty_value.mixed_v == nullptr) {
+ ctx.empty_value.mixed_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ *reinterpret_cast(ctx.empty_value.mixed_v) = T{};
+ return *reinterpret_cast(ctx.empty_value.mixed_v);
+ } else if constexpr (std::is_same_v>) {
+ if (ctx.empty_value.array_v == nullptr) {
+ ctx.empty_value.array_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+ }
+ *reinterpret_cast(ctx.empty_value.array_v) = T{};
+ return *reinterpret_cast(ctx.empty_value.array_v);
+ } else if constexpr (is_type_acceptable_for_mixed::value) {
+ using type2value_t = kphp::stl::unordered_map;
+ if (ctx.empty_value.objects == nullptr) {
+ auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(type2value_t))};
+ php_assert(raw_mem);
+ ctx.empty_value.objects = new (raw_mem) type2value_t{};
+ }
- using type2value_t = kphp::stl::unordered_map;
- if (ctx.empty_values == nullptr) {
- auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(type2value_t))};
- php_assert(raw_mem);
- ctx.empty_values = new (raw_mem) type2value_t{};
- reinterpret_cast(ctx.empty_values)->reserve(6); // bool, int64_t, double, string, mixed, array
- }
+ auto* type2value{reinterpret_cast(ctx.empty_value.objects)};
+ const auto type_id{std::type_index{typeid(T)}};
+ const auto it{type2value->find(type_id)};
+ if (it != type2value->end()) {
+ *reinterpret_cast(it->second) = T{};
+ return *reinterpret_cast(it->second);
+ }
- auto* type2value{reinterpret_cast(ctx.empty_values)};
- const auto it{type2value->find(std::type_index(typeid(T)))};
- if (it != type2value->end()) {
- *reinterpret_cast(it->second) = T{};
- return *reinterpret_cast(it->second);
+ auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
+ php_assert(raw_mem);
+ return *reinterpret_cast(type2value->insert({type_id, new (raw_mem) T{}}).first->second);
+ } else {
+ static_assert(vk::dependent_false_v, "Unsupported type provided");
}
-
- auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
- php_assert(raw_mem);
- return *reinterpret_cast(type2value->insert({std::type_index(typeid(T)), new (raw_mem) T{}}).first->second);
}
inline void mixed::reset_empty_values() noexcept {
- RuntimeContext::get().empty_values = nullptr;
+ auto& ctx{RuntimeContext::get()};
+ ctx.empty_value.bool_v = nullptr;
+ ctx.empty_value.int64_v = nullptr;
+ ctx.empty_value.double_v = nullptr;
+ ctx.empty_value.string_v = nullptr;
+ ctx.empty_value.mixed_v = nullptr;
+ ctx.empty_value.array_v = nullptr;
+ ctx.empty_value.objects = nullptr;
}
template
From cb8e53e2d76bc62ba8ce18ca7d3c2e70e4423c74 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Mon, 13 Jul 2026 16:39:12 +0300
Subject: [PATCH 4/7] Remove useless type trait
Signed-off-by: Petr Shumilov
---
common/type_traits/dependent_false.h | 17 -----------------
.../core/core-types/definition/mixed.inl | 5 +----
2 files changed, 1 insertion(+), 21 deletions(-)
delete mode 100644 common/type_traits/dependent_false.h
diff --git a/common/type_traits/dependent_false.h b/common/type_traits/dependent_false.h
deleted file mode 100644
index 00174a5934..0000000000
--- a/common/type_traits/dependent_false.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Compiler for PHP (aka KPHP)
-// Copyright (c) 2020 LLC «V Kontakte»
-// Distributed under the GPL v3 License, see LICENSE.notice.txt
-
-#pragma once
-
-#include
-
-namespace vk {
-
-template
-struct dependent_false : std::false_type {};
-
-template
-inline constexpr bool dependent_false_v = dependent_false::value;
-
-} // namespace vk
diff --git a/runtime-common/core/core-types/definition/mixed.inl b/runtime-common/core/core-types/definition/mixed.inl
index 3665bd548c..6743bf9199 100644
--- a/runtime-common/core/core-types/definition/mixed.inl
+++ b/runtime-common/core/core-types/definition/mixed.inl
@@ -9,7 +9,6 @@
#include "common/algorithms/find.h"
#include "common/smart_ptrs/intrusive_ptr.h"
-#include "common/type_traits/dependent_false.h"
#include "runtime-common/core/allocator/script-allocator.h"
#include "runtime-common/core/class-instance/refcountable-php-classes.h"
@@ -222,7 +221,7 @@ T& mixed::empty_value() noexcept {
}
*reinterpret_cast(ctx.empty_value.array_v) = T{};
return *reinterpret_cast(ctx.empty_value.array_v);
- } else if constexpr (is_type_acceptable_for_mixed::value) {
+ } else {
using type2value_t = kphp::stl::unordered_map;
if (ctx.empty_value.objects == nullptr) {
auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(type2value_t))};
@@ -241,8 +240,6 @@ T& mixed::empty_value() noexcept {
auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
php_assert(raw_mem);
return *reinterpret_cast(type2value->insert({type_id, new (raw_mem) T{}}).first->second);
- } else {
- static_assert(vk::dependent_false_v, "Unsupported type provided");
}
}
From f13867c791433992df2e0098688bb45ac1180ea7 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Mon, 13 Jul 2026 16:55:23 +0300
Subject: [PATCH 5/7] Fix code style
Signed-off-by: Petr Shumilov
---
runtime-common/core/core-types/definition/mixed.inl | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/runtime-common/core/core-types/definition/mixed.inl b/runtime-common/core/core-types/definition/mixed.inl
index 6743bf9199..94439d1902 100644
--- a/runtime-common/core/core-types/definition/mixed.inl
+++ b/runtime-common/core/core-types/definition/mixed.inl
@@ -232,14 +232,14 @@ T& mixed::empty_value() noexcept {
auto* type2value{reinterpret_cast(ctx.empty_value.objects)};
const auto type_id{std::type_index{typeid(T)}};
const auto it{type2value->find(type_id)};
- if (it != type2value->end()) {
- *reinterpret_cast(it->second) = T{};
- return *reinterpret_cast(it->second);
+ if (it == type2value->end()) {
+ auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
+ php_assert(raw_mem);
+ return *reinterpret_cast(type2value->insert({type_id, new (raw_mem) T{}}).first->second);
}
- auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
- php_assert(raw_mem);
- return *reinterpret_cast(type2value->insert({type_id, new (raw_mem) T{}}).first->second);
+ *reinterpret_cast(it->second) = T{};
+ return *reinterpret_cast(it->second);
}
}
From a043efb21a93c412d8c6fc145424a56e6e754aa1 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Mon, 13 Jul 2026 17:47:03 +0300
Subject: [PATCH 6/7] Add test
Signed-off-by: Petr Shumilov
---
.../001_set_value_for_empty_value.php | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 tests/phpt/mixed/primitive/001_set_value_for_empty_value.php
diff --git a/tests/phpt/mixed/primitive/001_set_value_for_empty_value.php b/tests/phpt/mixed/primitive/001_set_value_for_empty_value.php
new file mode 100644
index 0000000000..86473bbbd5
--- /dev/null
+++ b/tests/phpt/mixed/primitive/001_set_value_for_empty_value.php
@@ -0,0 +1,43 @@
+@ok
+ $v) {
+ // ($arr[$i]['j']) -- recv
+ ($arr[$i]['j'])['k'] = true;
+ }
+}
+
+$try_to_set_value_if_recv_is_string = function () {
+ /** @var mixed[] $arr */
+ $arr = ["foo"];
+
+ foreach ($arr as $i => $v) {
+ // ($arr[$i]['j']) -- recv
+ ($arr[$i]['j'])['k'] = true;
+ }
+};
+
+class C{}
+
+$try_to_set_value_if_recv_is_object = function() {
+ /** @var mixed[] $arr */
+ $arr = [to_mixed(new C())];
+
+ foreach ($arr as $i => $v) {
+ // ($arr[$i]['j']) -- recv
+ ($arr[$i]['j'])['k'] = true;
+ }
+};
+
+
+
+try_to_set_value_if_recv_is_int();
+#ifndef KPHP
+$try_to_set_value_if_recv_is_string = function (){};
+$try_to_set_value_if_recv_is_object = function (){};
+#endif
+$try_to_set_value_if_recv_is_string();
+$try_to_set_value_if_recv_is_object();
From 47e9636d49741a664612e3260bc64a093ba1f643 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Mon, 13 Jul 2026 19:53:02 +0300
Subject: [PATCH 7/7] Fix code style
Signed-off-by: Petr Shumilov
---
runtime-common/core/core-context.h | 18 +++---
.../core/core-types/definition/mixed.inl | 62 +++++++------------
2 files changed, 35 insertions(+), 45 deletions(-)
diff --git a/runtime-common/core/core-context.h b/runtime-common/core/core-context.h
index 8e40f9b683..eefc924655 100644
--- a/runtime-common/core/core-context.h
+++ b/runtime-common/core/core-context.h
@@ -9,6 +9,10 @@
#include "common/mixin/not_copyable.h"
+#include "runtime-common/core/allocator/script-allocator.h"
+#include "runtime-common/core/runtime-core.h"
+#include "runtime-common/core/std/containers.h"
+
#ifndef INCLUDED_FROM_KPHP_CORE
#error "this file must be included only from runtime-core.h"
#endif
@@ -18,13 +22,13 @@ struct RuntimeContext final : vk::not_copyable {
int32_t php_disable_warnings{};
uint32_t empty_obj_count{};
struct {
- void* bool_v{};
- void* int64_v{};
- void* double_v{};
- void* string_v{};
- void* mixed_v{};
- void* array_v{};
- void* objects{};
+ bool* bool_v{};
+ int64_t* int64_v{};
+ double* double_v{};
+ string* string_v{};
+ mixed* mixed_v{};
+ array* array_v{};
+ kphp::stl::unordered_map* objects{};
} empty_value;
string_buffer_lib_context sb_lib_context{};
diff --git a/runtime-common/core/core-types/definition/mixed.inl b/runtime-common/core/core-types/definition/mixed.inl
index 94439d1902..6fa50d2978 100644
--- a/runtime-common/core/core-types/definition/mixed.inl
+++ b/runtime-common/core/core-types/definition/mixed.inl
@@ -5,7 +5,7 @@
#pragma once
#include
-#include
+#include
#include "common/algorithms/find.h"
#include "common/smart_ptrs/intrusive_ptr.h"
@@ -183,59 +183,45 @@ inline void swap(mixed& lhs, mixed& rhs) {
template
T& mixed::empty_value() noexcept {
static_assert(vk::is_type_in_list>{} || is_type_acceptable_for_mixed::value, "unsupported type");
-
auto& ctx{RuntimeContext::get()};
- if constexpr (std::is_same_v) {
- if (ctx.empty_value.bool_v == nullptr) {
- ctx.empty_value.bool_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
+
+ const auto init_empty_value{[](T*& v) noexcept -> T& {
+ if (v == nullptr) {
+ auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
+ php_assert(raw_mem);
+ v = new (raw_mem) T{};
}
- *reinterpret_cast(ctx.empty_value.bool_v) = T{};
- return *reinterpret_cast(ctx.empty_value.bool_v);
+ *reinterpret_cast(v) = T{};
+ return *reinterpret_cast(v);
+ }};
+
+ if constexpr (std::is_same_v) {
+ return init_empty_value(ctx.empty_value.bool_v);
} else if constexpr (std::is_same_v) {
- if (ctx.empty_value.int64_v == nullptr) {
- ctx.empty_value.int64_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- *reinterpret_cast(ctx.empty_value.int64_v) = T{};
- return *reinterpret_cast(ctx.empty_value.int64_v);
+ return init_empty_value(ctx.empty_value.int64_v);
} else if constexpr (std::is_same_v) {
- if (ctx.empty_value.double_v == nullptr) {
- ctx.empty_value.double_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- *reinterpret_cast(ctx.empty_value.double_v) = T{};
- return *reinterpret_cast(ctx.empty_value.double_v);
+ return init_empty_value(ctx.empty_value.double_v);
} else if constexpr (std::is_same_v) {
- if (ctx.empty_value.string_v == nullptr) {
- ctx.empty_value.string_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- *reinterpret_cast(ctx.empty_value.string_v) = T{};
- return *reinterpret_cast(ctx.empty_value.string_v);
+ return init_empty_value(ctx.empty_value.string_v);
} else if constexpr (std::is_same_v) {
- if (ctx.empty_value.mixed_v == nullptr) {
- ctx.empty_value.mixed_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- *reinterpret_cast(ctx.empty_value.mixed_v) = T{};
- return *reinterpret_cast(ctx.empty_value.mixed_v);
+ return init_empty_value(ctx.empty_value.mixed_v);
} else if constexpr (std::is_same_v>) {
- if (ctx.empty_value.array_v == nullptr) {
- ctx.empty_value.array_v = new (RuntimeAllocator::get().alloc_script_memory(sizeof(T))) T{};
- }
- *reinterpret_cast(ctx.empty_value.array_v) = T{};
- return *reinterpret_cast(ctx.empty_value.array_v);
+ return init_empty_value(ctx.empty_value.array_v);
} else {
- using type2value_t = kphp::stl::unordered_map;
+ using type2value_t = kphp::stl::unordered_map;
if (ctx.empty_value.objects == nullptr) {
auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(type2value_t))};
php_assert(raw_mem);
ctx.empty_value.objects = new (raw_mem) type2value_t{};
}
- auto* type2value{reinterpret_cast(ctx.empty_value.objects)};
- const auto type_id{std::type_index{typeid(T)}};
- const auto it{type2value->find(type_id)};
- if (it == type2value->end()) {
+ static const char dummy = 0;
+ const void* type_id{&dummy};
+ const auto it{ctx.empty_value.objects->find(type_id)};
+ if (it == ctx.empty_value.objects->end()) {
auto* raw_mem{RuntimeAllocator::get().alloc_script_memory(sizeof(T))};
php_assert(raw_mem);
- return *reinterpret_cast(type2value->insert({type_id, new (raw_mem) T{}}).first->second);
+ return *reinterpret_cast(ctx.empty_value.objects->insert({type_id, new (raw_mem) T{}}).first->second);
}
*reinterpret_cast(it->second) = T{};