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); + } } }