diff --git a/array.c b/array.c index dbcc09d22b..02c963618a 100644 --- a/array.c +++ b/array.c @@ -5998,6 +5998,44 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) } /* + * call-seq: + * +ary -> ary (mutable) + * + * If the array is frozen, then return duplicated mutable array. + * + * If the array is not frozen, then return the array itself. + */ +static VALUE +rb_ary_uplus(VALUE ary) +{ + if (OBJ_FROZEN(ary)) { + return rb_ary_dup(ary); + } + else { + return ary; + } +} + +/* + * call-seq: + * -ary -> ary (frozen) + * + * If the array is frozen, then return the array itself. + * + * If the array is not frozen, return a frozen copy of it. + */ +static VALUE +rb_ary_uminus(VALUE ary) +{ + if (OBJ_FROZEN(ary)) { + return ary; + } + else { + return rb_ary_freeze(ary); + } +} + +/* * Arrays are ordered, integer-indexed collections of any object. * * Array indexing starts at 0, as in C or Java. A negative index is assumed @@ -6358,5 +6396,8 @@ Init_Array(void) rb_define_method(rb_cArray, "dig", rb_ary_dig, -1); rb_define_method(rb_cArray, "sum", rb_ary_sum, -1); + rb_define_method(rb_cArray, "+@", rb_ary_uplus, 0); + rb_define_method(rb_cArray, "-@", rb_ary_uminus, 0); + id_random = rb_intern("random"); } diff --git a/hash.c b/hash.c index 3a499872ad..3810e020fa 100644 --- a/hash.c +++ b/hash.c @@ -3179,6 +3179,44 @@ rb_hash_gt(VALUE hash, VALUE other) return hash_le(other, hash); } +/* + * call-seq: + * +hsh -> hsh (mutable) + * + * If the hash is frozen, then return duplicated mutable hash. + * + * If the hash is not frozen, then return the hash itself. + */ +static VALUE +rb_hash_uplus(VALUE hsh) +{ + if (OBJ_FROZEN(hsh)) { + return rb_hash_dup(hsh); + } + else { + return hsh; + } +} + +/* + * call-seq: + * -hsh -> hsh (frozen) + * + * If the hash is frozen, then return the hash itself. + * + * If the hash is not frozen, return a frozen copy of it. + */ +static VALUE +rb_hash_uminus(VALUE hsh) +{ + if (OBJ_FROZEN(hsh)) { + return hsh; + } + else { + return rb_hash_freeze(hsh); + } +} + static VALUE hash_proc_call(VALUE key, VALUE hash, int argc, const VALUE *argv, VALUE passed_proc) { @@ -4725,6 +4763,9 @@ Init_Hash(void) rb_define_method(rb_cHash, ">=", rb_hash_ge, 1); rb_define_method(rb_cHash, ">", rb_hash_gt, 1); + rb_define_method(rb_cHash, "+@", rb_hash_uplus, 0); + rb_define_method(rb_cHash, "-@", rb_hash_uminus, 0); + /* Document-class: ENV * * ENV is a hash-like accessor for environment variables. diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index 6f4999386f..b3a8c54862 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -2988,6 +2988,26 @@ def test_sum assert_raise(TypeError) {[1].sum("")} end + def test_uplus + a = [1, 2, 3].freeze + + assert a.frozen? + refute (+a).frozen? + + a = [1, 2, 3] + assert_equal a, +a + end + + def test_uminus + a = [1, 2, 3] + + refute a.frozen? + assert (-a).frozen? + + a = [1, 2, 3].freeze + assert_equal a, -a + end + private def need_continuation unless respond_to?(:callcc, true) diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 088178defa..b6665d8772 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1652,6 +1652,26 @@ def test_broken_hash_value assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; 0 + a + b != 0 + b + a}, bug14218) end + def test_uplus + a = { a: 1, b: 2, c: 3 }.freeze + + assert a.frozen? + refute (+a).frozen? + + a = { a: 1, b: 2, c: 3 } + assert_equal a, +a + end + + def test_uminus + a = { a: 1, b: 2, c: 3 } + + refute a.frozen? + assert (-a).frozen? + + a = { a: 1, b: 2, c: 3 }.freeze + assert_equal a, -a + end + class TestSubHash < TestHash class SubHash < Hash def reject(*)