diff --git a/NEWS.md b/NEWS.md
index 288f7d97dedf93..964bafacd65aa2 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -47,6 +47,10 @@ Note: We're only listing outstanding class updates.
* `IO.select` accepts +Float::INFINITY+ as a timeout argument.
[[Feature #20610]]
+* Math
+
+ * `Math.log1p` and `Math.expm1` are added. [[Feature #21527]]
+
* Socket
* `Socket.tcp` & `TCPSocket.new` accepts `open_timeout` as a keyword argument to specify
@@ -254,3 +258,4 @@ The following bundled gems are updated.
[Feature #21287]: https://bugs.ruby-lang.org/issues/21287
[Feature #21347]: https://bugs.ruby-lang.org/issues/21347
[Feature #21360]: https://bugs.ruby-lang.org/issues/21360
+[Feature #21527]: https://bugs.ruby-lang.org/issues/21527
diff --git a/configure.ac b/configure.ac
index fdff2469cb1240..82f144a8bbdff5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1680,21 +1680,6 @@ AS_IF([test "$rb_cv_func_weak" != x], [
AC_DEFINE(HAVE_FUNC_WEAK)
])
-AC_CACHE_CHECK([for __attribute__((__deprecated__(msg))) in C++],
- rb_cv_CentOS6_CXX_workaround,
- RUBY_WERROR_FLAG([
- AC_LANG_PUSH([C++])
- AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM(
- [],
- [__attribute__((__deprecated__("message"))) int conftest(...);])],
- [rb_cv_CentOS6_CXX_workaround=yes],
- [rb_cv_CentOS6_CXX_workaround=no])
- AC_LANG_POP()]))
-AS_IF([test "$rb_cv_CentOS6_CXX_workaround" != no],[
- AC_DEFINE([RUBY_CXX_DEPRECATED(msg)],
- [__attribute__((__deprecated__(msg)))])])
-
AC_CACHE_CHECK([for std::nullptr_t], rb_cv_CXX_nullptr, [
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE(
diff --git a/depend b/depend
index 0fcf5ce652cd06..334bab56848172 100644
--- a/depend
+++ b/depend
@@ -6306,6 +6306,7 @@ imemo.$(OBJEXT): $(top_srcdir)/internal/compilers.h
imemo.$(OBJEXT): $(top_srcdir)/internal/gc.h
imemo.$(OBJEXT): $(top_srcdir)/internal/imemo.h
imemo.$(OBJEXT): $(top_srcdir)/internal/namespace.h
+imemo.$(OBJEXT): $(top_srcdir)/internal/object.h
imemo.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
imemo.$(OBJEXT): $(top_srcdir)/internal/serial.h
imemo.$(OBJEXT): $(top_srcdir)/internal/set_table.h
@@ -7159,7 +7160,6 @@ iseq.$(OBJEXT): $(top_srcdir)/prism/pack.h
iseq.$(OBJEXT): $(top_srcdir)/prism/parser.h
iseq.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h
-iseq.$(OBJEXT): $(top_srcdir)/prism/prism.h
iseq.$(OBJEXT): $(top_srcdir)/prism/regexp.h
iseq.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
iseq.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
@@ -11676,7 +11676,6 @@ prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h
-prism/prism.$(OBJEXT): $(top_srcdir)/prism/version.h
prism/regexp.$(OBJEXT): $(top_srcdir)/prism/ast.h
prism/regexp.$(OBJEXT): $(top_srcdir)/prism/defines.h
prism/regexp.$(OBJEXT): $(top_srcdir)/prism/encoding.h
diff --git a/imemo.c b/imemo.c
index fde5b15ad6ae9c..2fde22a3db865b 100644
--- a/imemo.c
+++ b/imemo.c
@@ -3,6 +3,7 @@
#include "id_table.h"
#include "internal.h"
#include "internal/imemo.h"
+#include "internal/object.h"
#include "internal/st.h"
#include "vm_callinfo.h"
@@ -208,6 +209,8 @@ rb_imemo_fields_clear(VALUE fields_obj)
else {
RBASIC_SET_SHAPE_ID(fields_obj, ROOT_SHAPE_ID);
}
+ // Invalidate the ec->gen_fields_cache.
+ RBASIC_CLEAR_CLASS(fields_obj);
}
/* =========================================================================
diff --git a/internal/class.h b/internal/class.h
index db4ae2ada1b462..bed69adef7cd1a 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -630,7 +630,7 @@ RCLASS_WRITE_CALLABLE_M_TBL(VALUE klass, struct rb_id_table *table)
static inline void
RCLASS_WRITE_CC_TBL(VALUE klass, VALUE table)
{
- RB_OBJ_WRITE(klass, &RCLASSEXT_CC_TBL(RCLASS_EXT_WRITABLE(klass)), table);
+ RB_OBJ_ATOMIC_WRITE(klass, &RCLASSEXT_CC_TBL(RCLASS_EXT_WRITABLE(klass)), table);
}
static inline void
diff --git a/math.c b/math.c
index a95919e3420e8e..9e96f3666a3bdd 100644
--- a/math.c
+++ b/math.c
@@ -457,6 +457,34 @@ math_exp(VALUE unused_obj, VALUE x)
return DBL2NUM(exp(Get_Double(x)));
}
+/*
+ * call-seq:
+ * Math.expm1(x) -> float
+ *
+ * Returns "exp(x) - 1", +e+ raised to the +x+ power, minus 1.
+ *
+ *
+ * - Domain: [-INFINITY, INFINITY].
+ * - Range: [-1.0, INFINITY].
+ *
+ * Examples:
+ *
+ * expm1(-INFINITY) # => 0.0
+ * expm1(-1.0) # => -0.6321205588285577 # 1.0/E - 1
+ * expm1(0.0) # => 0.0
+ * expm1(0.5) # => 0.6487212707001282 # sqrt(E) - 1
+ * expm1(1.0) # => 1.718281828459045 # E - 1
+ * expm1(2.0) # => 6.38905609893065 # E**2 - 1
+ * expm1(INFINITY) # => Infinity
+ *
+ */
+
+static VALUE
+math_expm1(VALUE unused_obj, VALUE x)
+{
+ return DBL2NUM(expm1(Get_Double(x)));
+}
+
#if defined __CYGWIN__
# include
# if CYGWIN_VERSION_DLL_MAJOR < 1005
@@ -646,6 +674,47 @@ math_log10(VALUE unused_obj, VALUE x)
return DBL2NUM(log10(d) + numbits * log10(2)); /* log10(d * 2 ** numbits) */
}
+/*
+ * call-seq:
+ * Math.log1p(x) -> float
+ *
+ * Returns "log(x + 1)", the base E {logarithm}[https://en.wikipedia.org/wiki/Logarithm] of (+x+ + 1).
+ *
+ * - Domain: [-1, INFINITY].
+ * - Range: [-INFINITY, INFINITY].
+ *
+ * Examples:
+ *
+ * log1p(-1.0) # => -Infinity
+ * log1p(0.0) # => 0.0
+ * log1p(E - 1) # => 1.0
+ * log1p(INFINITY) # => Infinity
+ *
+ */
+
+static VALUE
+math_log1p(VALUE unused_obj, VALUE x)
+{
+ size_t numbits;
+ double d = get_double_rshift(x, &numbits);
+
+ if (numbits != 0) {
+ x = rb_big_plus(x, INT2FIX(1));
+ d = math_log_split(x, &numbits);
+ /* check for pole error */
+ if (d == 0.0) return DBL2NUM(-HUGE_VAL);
+ d = log(d);
+ d += numbits * M_LN2;
+ return DBL2NUM(d);
+ }
+
+ domain_check_min(d, -1.0, "log1p");
+ /* check for pole error */
+ if (d == -1.0) return DBL2NUM(-HUGE_VAL);
+
+ return DBL2NUM(log1p(d)); /* log10(d * 2 ** numbits) */
+}
+
static VALUE rb_math_sqrt(VALUE x);
/*
@@ -1120,9 +1189,11 @@ InitVM_Math(void)
rb_define_module_function(rb_mMath, "atanh", math_atanh, 1);
rb_define_module_function(rb_mMath, "exp", math_exp, 1);
+ rb_define_module_function(rb_mMath, "expm1", math_expm1, 1);
rb_define_module_function(rb_mMath, "log", math_log, -1);
rb_define_module_function(rb_mMath, "log2", math_log2, 1);
rb_define_module_function(rb_mMath, "log10", math_log10, 1);
+ rb_define_module_function(rb_mMath, "log1p", math_log1p, 1);
rb_define_module_function(rb_mMath, "sqrt", math_sqrt, 1);
rb_define_module_function(rb_mMath, "cbrt", math_cbrt, 1);
diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb
index 6e67099c6b063f..a676bb5cd9ab84 100644
--- a/test/ruby/test_math.rb
+++ b/test/ruby/test_math.rb
@@ -147,6 +147,13 @@ def test_exp
check(Math::E ** 2, Math.exp(2))
end
+ def test_expm1
+ check(0, Math.expm1(0))
+ check(Math.sqrt(Math::E) - 1, Math.expm1(0.5))
+ check(Math::E - 1, Math.expm1(1))
+ check(Math::E ** 2 - 1, Math.expm1(2))
+ end
+
def test_log
check(0, Math.log(1))
check(1, Math.log(Math::E))
@@ -201,6 +208,19 @@ def test_log10
assert_nothing_raised { assert_infinity(-Math.log10(0)) }
end
+ def test_log1p
+ check(0, Math.log1p(0))
+ check(1, Math.log1p(Math::E - 1))
+ check(Math.log(2.0 ** 64 + 1), Math.log1p(1 << 64))
+ check(Math.log(2) * 1024.0, Math.log1p(2 ** 1024))
+ assert_nothing_raised { assert_infinity(Math.log1p(1.0/0)) }
+ assert_nothing_raised { assert_infinity(-Math.log1p(-1.0)) }
+ assert_raise_with_message(Math::DomainError, /\blog1p\b/) { Math.log1p(-1.1) }
+ assert_raise_with_message(Math::DomainError, /\blog1p\b/) { Math.log1p(-Float::EPSILON-1) }
+ assert_nothing_raised { assert_nan(Math.log1p(Float::NAN)) }
+ assert_nothing_raised { assert_infinity(-Math.log1p(-1)) }
+ end
+
def test_sqrt
check(0, Math.sqrt(0))
check(1, Math.sqrt(1))
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb
index cc784e7644fdf3..3504b9b7dc3131 100644
--- a/test/ruby/test_variable.rb
+++ b/test/ruby/test_variable.rb
@@ -447,6 +447,19 @@ def test_local_variables_encoding
assert_equal(%i[α b], b.local_variables)
end
+ def test_genivar_cache
+ bug21547 = '[Bug #21547]'
+ klass = Class.new(Array)
+ instance = klass.new
+ instance.instance_variable_set(:@a1, 1)
+ instance.instance_variable_set(:@a2, 2)
+ Fiber.new do
+ instance.instance_variable_set(:@a3, 3)
+ instance.instance_variable_set(:@a4, 4)
+ end.resume
+ assert_equal 4, instance.instance_variable_get(:@a4)
+ end
+
private
def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:)
local_variables
diff --git a/variable.c b/variable.c
index 4f0f83d20343fb..1cd1c604c34319 100644
--- a/variable.c
+++ b/variable.c
@@ -1247,7 +1247,7 @@ rb_obj_fields(VALUE obj, ID field_name)
generic_fields:
{
rb_execution_context_t *ec = GET_EC();
- if (ec->gen_fields_cache.obj == obj) {
+ if (ec->gen_fields_cache.obj == obj && rb_imemo_fields_owner(ec->gen_fields_cache.fields_obj) == obj) {
fields_obj = ec->gen_fields_cache.fields_obj;
}
else {
diff --git a/vm_method.c b/vm_method.c
index 73a431ce93602c..241e4cacefdce4 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -1786,7 +1786,14 @@ cache_callable_method_entry(VALUE klass, ID mid, const rb_callable_method_entry_
#endif
}
else {
- vm_ccs_create(klass, cc_tbl, mid, cme);
+ if (rb_multi_ractor_p()) {
+ VALUE new_cc_tbl = rb_vm_cc_table_dup(cc_tbl);
+ vm_ccs_create(klass, new_cc_tbl, mid, cme);
+ RB_OBJ_ATOMIC_WRITE(klass, &RCLASSEXT_CC_TBL(RCLASS_EXT_WRITABLE(klass)), new_cc_tbl);
+ }
+ else {
+ vm_ccs_create(klass, cc_tbl, mid, cme);
+ }
}
}