Skip to content

Use RUBY_DEBUG instead of NDEBUG #3124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 25, 2020
Merged

Conversation

ko1
Copy link
Contributor

@ko1 ko1 commented May 20, 2020

Assertions in header files slows down an interpreter, so they should be
turned off by default (simple make). To enable them, define a macro
RUBY_DEBUG=1 (e.g. make cppflags=-DRUBY_DEBUG or use #define at
the very beggining of the file. Note that even if NDEBUG=1 is defined,
RUBY_DEBUG=1 enables all assertions.
[Feature #16837]
related: #3120

assert() lines in MRI *.c is not disabled even if RUBY_DEBUG=0 and
it can be disabled with NDEBUG=1. So please consider to use
RUBY_ASSERT() if you want to disable them when RUBY_DEBUG=0.

Assertions in header files slows down an interpreter, so they should be
turned off by default (simple `make`). To enable them, define a macro
`RUBY_DEBUG=1` (e.g. `make cppflags=-DRUBY_DEBUG` or use `#define` at
the very beggining of the file. Note that even if `NDEBUG=1` is defined,
`RUBY_DEBUG=1` enables all assertions.
[Feature #16837]
related: ruby#3120

`assert()` lines in MRI *.c is not disabled even if `RUBY_DEBUG=0` and
it can be disabled with `NDEBUG=1`. So please consider to use
`RUBY_ASSERT()` if you want to disable them when `RUBY_DEBUG=0`.
@ko1 ko1 requested review from shyouhei and nobu and removed request for shyouhei May 20, 2020 02:46
@shyouhei shyouhei mentioned this pull request May 20, 2020
@shyouhei
Copy link
Member

Correct me if I'm wrong. What we want to achieve is:

  1. You want speed. Running fast is more important than running right. No assertions should ever creep into the generated binary, until you are aware of the needs of sanity.
  2. You want to control assertions file-by-file. I don't do that but perhaps you occasionally write #define RUBY_DEBUG 1 at the top of vm.c. You want that way.
  3. You cannot eliminate assert to migrate to RUBY_ASSERT, because there are submodules using assert (see NDEBUG #3120 (comment)).

Given the criteria, I think it is inevitable to either define or undefine NDEBUG according to RUBY_DEBUG. Thoughts?

@ko1
Copy link
Contributor Author

ko1 commented May 20, 2020

For submodules, we can consider later, IMO.
In other words, this patch disables newly introduce assertions into header files which have huge impact on performance.

The reasons of performance impact are, frequency (RB_TYPE_P, for example) and duplicated assertions (nested functions checks same pre-conditions, for example).

@ko1
Copy link
Contributor Author

ko1 commented May 20, 2020

I think the current assertion (pre-condition/post-condition checks and so on) is not enough (e.g. range check for RARRAY_AREF). However, to introduce more checks, the performance will be a barrier if enabled by default. And I believe proposed policy (at least for headers) is good to compromise.

Co-authored-by: 卜部昌平 <shyouhei@ruby-lang.org>
@shyouhei
Copy link
Member

I have implemented what I thought to be ideal. You don't like it. OK, then what do you think is ideal instead?

In other words, this patch disables newly introduce assertions into header files which have huge impact on performance.

It sounds too short-sighted. Incremental approach itself isn't bad, but needs a solid goal. Otherwise the implementation drifts away.

Also (not important):

The reasons of performance impact are, frequency (RB_TYPE_P, for example) and duplicated assertions (nested functions checks same pre-conditions, for example).

I don't think so. As I wrote in [ruby-core:98183], assertions themselves are lightweight. I think increased function calling overhead is the key. Do you have any analysis?

@ko1
Copy link
Contributor Author

ko1 commented May 21, 2020

Also (not important):

The reasons of performance impact are, frequency (RB_TYPE_P, for example) and duplicated assertions (nested functions checks same pre-conditions, for example).

I don't think so. As I wrote in [ruby-core:98183], assertions themselves are lightweight. I think increased function calling overhead is the key. Do you have any analysis?

OK, I haven't measure so that I tried with rb_ary_entry().

gcc 7.5.0 (Ubuntu 18.04 default) generates the following asm:

0000000000012770 <rb_ary_entry>:
   12770:       41 54                   push   %r12
   12772:       55                      push   %rbp
   12773:       48 89 f5                mov    %rsi,%rbp
   12776:       53                      push   %rbx
   12777:       48 89 fb                mov    %rdi,%rbx
   1277a:       e8 81 de fe ff          callq  600 <rb_array_len>
   1277f:       f6 c3 07                test   $0x7,%bl
   12782:       49 89 c4                mov    %rax,%r12
   12785:       74 59                   je     127e0 <rb_ary_entry+0x70>
   12787:       be 07 00 00 00          mov    $0x7,%esi
   1278c:       48 89 df                mov    %rbx,%rdi
   1278f:       e8 00 00 00 00          callq  12794 <rb_ary_entry+0x24>
                        12790: R_X86_64_PLT32   rb_check_type-0x4
   12794:       be 00 20 00 00          mov    $0x2000,%esi
   12799:       48 89 df                mov    %rbx,%rdi
   1279c:       e8 ff dc fe ff          callq  4a0 <RB_FL_TEST_RAW>
   127a1:       48 85 c0                test   %rax,%rax
   127a4:       75 2a                   jne    127d0 <rb_ary_entry+0x60>
   127a6:       48 8b 5b 20             mov    0x20(%rbx),%rbx
   127aa:       4d 85 e4                test   %r12,%r12
   127ad:       b8 08 00 00 00          mov    $0x8,%eax
   127b2:       74 0e                   je     127c2 <rb_ary_entry+0x52>
   127b4:       48 85 ed                test   %rbp,%rbp
   127b7:       78 47                   js     12800 <rb_ary_entry+0x90>
   127b9:       4c 39 e5                cmp    %r12,%rbp
   127bc:       7d 04                   jge    127c2 <rb_ary_entry+0x52>
   127be:       48 8b 04 eb             mov    (%rbx,%rbp,8),%rax
   127c2:       5b                      pop    %rbx
   127c3:       5d                      pop    %rbp
   127c4:       41 5c                   pop    %r12
   127c6:       c3                      retq
   127c7:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
   127ce:       00 00
   127d0:       48 83 c3 10             add    $0x10,%rbx
   127d4:       eb d4                   jmp    127aa <rb_ary_entry+0x3a>
   127d6:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
   127dd:       00 00 00
   127e0:       48 f7 c3 f7 ff ff ff    test   $0xfffffffffffffff7,%rbx
   127e7:       74 9e                   je     12787 <rb_ary_entry+0x17>
   127e9:       48 89 df                mov    %rbx,%rdi
   127ec:       e8 bf d8 fe ff          callq  b0 <RB_BUILTIN_TYPE>
   127f1:       83 f8 07                cmp    $0x7,%eax
   127f4:       75 91                   jne    12787 <rb_ary_entry+0x17>
   127f6:       eb 9c                   jmp    12794 <rb_ary_entry+0x24>
   127f8:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
   127ff:       00
   12800:       4c 01 e5                add    %r12,%rbp
   12803:       79 b9                   jns    127be <rb_ary_entry+0x4e>
   12805:       eb bb                   jmp    127c2 <rb_ary_entry+0x52>
   12807:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
   1280e:       00 00

To inline several functions, I introduced forceinline primitives:

 #include "array.rbinc"
diff --git a/include/ruby/internal/core/rarray.h b/include/ruby/internal/core/rarray.h
index ea2abe6618..4cab5e4234 100644
--- a/include/ruby/internal/core/rarray.h
+++ b/include/ruby/internal/core/rarray.h
@@ -114,6 +114,7 @@ RBIMPL_SYMBOL_EXPORT_END()
 
 RBIMPL_ATTR_PURE_ON_NDEBUG()
 RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_FORCEINLINE()
 static inline long
 RARRAY_EMBED_LEN(VALUE ary)
 {
@@ -126,6 +127,7 @@ RARRAY_EMBED_LEN(VALUE ary)
     return RBIMPL_CAST((long)f);
 }
 
+RBIMPL_ATTR_FORCEINLINE()
 RBIMPL_ATTR_PURE_ON_NDEBUG()
 static inline long
 rb_array_len(VALUE a)
diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h
index 88a13b87f5..21b67a4dfd 100644
--- a/include/ruby/internal/fl_type.h
+++ b/include/ruby/internal/fl_type.h
@@ -226,6 +226,8 @@ RB_FL_ABLE(VALUE obj)
     }
 }
 
+
+RBIMPL_ATTR_FORCEINLINE()
 RBIMPL_ATTR_PURE_ON_NDEBUG()
 RBIMPL_ATTR_ARTIFICIAL()
 static inline VALUE
@@ -250,6 +252,7 @@ RB_FL_TEST(VALUE obj, VALUE flags)
 
 RBIMPL_ATTR_PURE_ON_NDEBUG()
 RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_FORCEINLINE()
 static inline bool
 RB_FL_ANY_RAW(VALUE obj, VALUE flags)
 {
diff --git a/include/ruby/internal/value_type.h b/include/ruby/internal/value_type.h
index 70a89a4854..77cffc38b9 100644
--- a/include/ruby/internal/value_type.h
+++ b/include/ruby/internal/value_type.h
@@ -149,6 +149,7 @@ RBIMPL_SYMBOL_EXPORT_END()
 
 RBIMPL_ATTR_PURE_ON_NDEBUG()
 RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_FORCEINLINE()
 static inline enum ruby_value_type
 RB_BUILTIN_TYPE(VALUE obj)
 {

I got the following asm:

# forceinline
0000000000019600 <rb_ary_entry>:
   19600:	41 54                	push   %r12
   19602:	55                   	push   %rbp
   19603:	53                   	push   %rbx
   19604:	48 89 fb             	mov    %rdi,%rbx
   19607:	83 e7 07             	and    $0x7,%edi
   1960a:	0f 85 90 00 00 00    	jne    196a0 <rb_ary_entry+0xa0>
   19610:	48 f7 c3 f7 ff ff ff 	test   $0xfffffffffffffff7,%rbx
   19617:	48 89 f5             	mov    %rsi,%rbp
   1961a:	0f 85 b0 00 00 00    	jne    196d0 <rb_ary_entry+0xd0>
   19620:	be 07 00 00 00       	mov    $0x7,%esi
   19625:	48 89 df             	mov    %rbx,%rdi
   19628:	e8 00 00 00 00       	callq  1962d <rb_ary_entry+0x2d>
			19629: R_X86_64_PLT32	rb_check_type-0x4
   1962d:	48 f7 c3 f7 ff ff ff 	test   $0xfffffffffffffff7,%rbx
   19634:	74 77                	je     196ad <rb_ary_entry+0xad>
   19636:	48 8b 03             	mov    (%rbx),%rax
   19639:	48 89 c2             	mov    %rax,%rdx
   1963c:	83 e2 1f             	and    $0x1f,%edx
   1963f:	48 83 fa 1b          	cmp    $0x1b,%rdx
   19643:	74 68                	je     196ad <rb_ary_entry+0xad>
   19645:	f6 c4 20             	test   $0x20,%ah
   19648:	0f 85 a2 00 00 00    	jne    196f0 <rb_ary_entry+0xf0>
   1964e:	4c 8b 63 10          	mov    0x10(%rbx),%r12
   19652:	48 83 fa 07          	cmp    $0x7,%rdx
   19656:	74 1c                	je     19674 <rb_ary_entry+0x74>
   19658:	be 07 00 00 00       	mov    $0x7,%esi
   1965d:	48 89 df             	mov    %rbx,%rdi
   19660:	e8 00 00 00 00       	callq  19665 <rb_ary_entry+0x65>
			19661: R_X86_64_PLT32	rb_check_type-0x4
   19665:	48 8b 03             	mov    (%rbx),%rax
   19668:	48 89 c2             	mov    %rax,%rdx
   1966b:	83 e2 1f             	and    $0x1f,%edx
   1966e:	48 83 fa 1b          	cmp    $0x1b,%rdx
   19672:	74 39                	je     196ad <rb_ary_entry+0xad>
   19674:	f6 c4 20             	test   $0x20,%ah
   19677:	75 6f                	jne    196e8 <rb_ary_entry+0xe8>
   19679:	48 8b 5b 20          	mov    0x20(%rbx),%rbx
   1967d:	4d 85 e4             	test   %r12,%r12
   19680:	b8 08 00 00 00       	mov    $0x8,%eax
   19685:	74 12                	je     19699 <rb_ary_entry+0x99>
   19687:	48 85 ed             	test   %rbp,%rbp
   1968a:	0f 88 c0 00 00 00    	js     19750 <rb_ary_entry+0x150>
   19690:	4c 39 e5             	cmp    %r12,%rbp
   19693:	7d 04                	jge    19699 <rb_ary_entry+0x99>
   19695:	48 8b 04 eb          	mov    (%rbx,%rbp,8),%rax
   19699:	5b                   	pop    %rbx
   1969a:	5d                   	pop    %rbp
   1969b:	41 5c                	pop    %r12
   1969d:	c3                   	retq   
   1969e:	66 90                	xchg   %ax,%ax
   196a0:	be 07 00 00 00       	mov    $0x7,%esi
   196a5:	48 89 df             	mov    %rbx,%rdi
   196a8:	e8 00 00 00 00       	callq  196ad <rb_ary_entry+0xad>
			196a9: R_X86_64_PLT32	rb_check_type-0x4
   196ad:	48 8d 0d 00 00 00 00 	lea    0x0(%rip),%rcx        # 196b4 <rb_ary_entry+0xb4>
			196b0: R_X86_64_PC32	.LC6-0x4
   196b4:	48 8d 15 00 00 00 00 	lea    0x0(%rip),%rdx        # 196bb <rb_ary_entry+0xbb>
			196b7: R_X86_64_PC32	.rodata+0x5fc
   196bb:	48 8d 3d 00 00 00 00 	lea    0x0(%rip),%rdi        # 196c2 <rb_ary_entry+0xc2>
			196be: R_X86_64_PC32	.LC7-0x4
   196c2:	be ec 00 00 00       	mov    $0xec,%esi
   196c7:	e8 00 00 00 00       	callq  196cc <rb_ary_entry+0xcc>
			196c8: R_X86_64_PLT32	rb_assert_failure-0x4
   196cc:	0f 1f 40 00          	nopl   0x0(%rax)
   196d0:	48 8b 03             	mov    (%rbx),%rax
   196d3:	83 e0 1f             	and    $0x1f,%eax
   196d6:	48 83 f8 07          	cmp    $0x7,%rax
   196da:	0f 85 40 ff ff ff    	jne    19620 <rb_ary_entry+0x20>
   196e0:	e9 51 ff ff ff       	jmpq   19636 <rb_ary_entry+0x36>
   196e5:	0f 1f 00             	nopl   (%rax)
   196e8:	48 83 c3 10          	add    $0x10,%rbx
   196ec:	eb 8f                	jmp    1967d <rb_ary_entry+0x7d>
   196ee:	66 90                	xchg   %ax,%ax
   196f0:	48 83 fa 07          	cmp    $0x7,%rdx
   196f4:	74 22                	je     19718 <rb_ary_entry+0x118>
   196f6:	be 07 00 00 00       	mov    $0x7,%esi
   196fb:	48 89 df             	mov    %rbx,%rdi
   196fe:	e8 00 00 00 00       	callq  19703 <rb_ary_entry+0x103>
			196ff: R_X86_64_PLT32	rb_check_type-0x4
   19703:	48 8b 03             	mov    (%rbx),%rax
   19706:	48 89 c2             	mov    %rax,%rdx
   19709:	83 e2 1f             	and    $0x1f,%edx
   1970c:	48 83 fa 1b          	cmp    $0x1b,%rdx
   19710:	74 9b                	je     196ad <rb_ary_entry+0xad>
   19712:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
   19718:	49 89 c4             	mov    %rax,%r12
   1971b:	49 c1 ec 0f          	shr    $0xf,%r12
   1971f:	41 83 e4 03          	and    $0x3,%r12d
   19723:	f6 c4 20             	test   $0x20,%ah
   19726:	0f 85 26 ff ff ff    	jne    19652 <rb_ary_entry+0x52>
   1972c:	48 8d 0d 00 00 00 00 	lea    0x0(%rip),%rcx        # 19733 <rb_ary_entry+0x133>
			1972f: R_X86_64_PC32	.LC8-0x4
   19733:	48 8d 15 00 00 00 00 	lea    0x0(%rip),%rdx        # 1973a <rb_ary_entry+0x13a>
			19736: R_X86_64_PC32	.rodata+0x55c
   1973a:	48 8d 3d 00 00 00 00 	lea    0x0(%rip),%rdi        # 19741 <rb_ary_entry+0x141>
			1973d: R_X86_64_PC32	.LC9-0x4
   19741:	be 7a 00 00 00       	mov    $0x7a,%esi
   19746:	e8 00 00 00 00       	callq  1974b <rb_ary_entry+0x14b>
			19747: R_X86_64_PLT32	rb_assert_failure-0x4
   1974b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
   19750:	4c 01 e5             	add    %r12,%rbp
   19753:	0f 89 3c ff ff ff    	jns    19695 <rb_ary_entry+0x95>
   19759:	e9 3b ff ff ff       	jmpq   19699 <rb_ary_entry+0x99>
   1975e:	66 90                	xchg   %ax,%ax

There are several rb_check_type code, but it was marked as cold, so I remained it.

With NDEBUG, rb_ary_entry() is compiled to:

#define NDEBUG
000000000000b100 <rb_ary_entry>:
    b100:       48 8b 17                mov    (%rdi),%rdx
    b103:       f6 c6 20                test   $0x20,%dh
    b106:       74 28                   je     b130 <rb_ary_entry+0x30>
    b108:       48 c1 ea 0f             shr    $0xf,%rdx
    b10c:       48 83 c7 10             add    $0x10,%rdi
    b110:       83 e2 03                and    $0x3,%edx
    b113:       48 85 d2                test   %rdx,%rdx
    b116:       b8 08 00 00 00          mov    $0x8,%eax
    b11b:       74 0e                   je     b12b <rb_ary_entry+0x2b>
    b11d:       48 85 f6                test   %rsi,%rsi
    b120:       78 1e                   js     b140 <rb_ary_entry+0x40>
    b122:       48 39 d6                cmp    %rdx,%rsi
    b125:       7d 04                   jge    b12b <rb_ary_entry+0x2b>
    b127:       48 8b 04 f7             mov    (%rdi,%rsi,8),%rax
    b12b:       f3 c3                   repz retq
    b12d:       0f 1f 00                nopl   (%rax)
    b130:       48 8b 57 10             mov    0x10(%rdi),%rdx
    b134:       48 8b 7f 20             mov    0x20(%rdi),%rdi
    b138:       eb d9                   jmp    b113 <rb_ary_entry+0x13>
    b13a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    b140:       48 01 d6                add    %rdx,%rsi
    b143:       79 e2                   jns    b127 <rb_ary_entry+0x27>
    b145:       f3 c3                   repz retq
    b147:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
    b14e:       00 00

I added a benchmark into array.c:

diff --git a/array.c b/array.c
index 90fad544f1..c57a12edb7 100644
--- a/array.c
+++ b/array.c
@@ -1,3 +1,4 @@
+#define NDEBUG
 /**********************************************************************
 
   array.c -
@@ -6966,6 +6967,8 @@ rb_ary_deconstruct(VALUE ary)
  *     arr                       #=> [1, 2, 3]
  */
 
+VALUE gv;
+
 void
 Init_Array(void)
 {
@@ -7094,6 +7097,13 @@ Init_Array(void)
     rb_define_method(rb_cArray, "sum", rb_ary_sum, -1);
 
     rb_define_method(rb_cArray, "deconstruct", rb_ary_deconstruct, 0);
+
+    int i=0;
+    VALUE ary = rb_ary_new_from_args(3, Qnil, Qnil, Qnil);
+
+    for (i=0; i<100*1000*1000; i++) {
+        gv = rb_ary_entry(ary, i%3);
+    }
 }

and the boot time of miniruby (./miniruby -e0) is:

master
real    0m2.958s
user    0m2.950s
sys     0m0.008s

force-inline
real    0m0.748s
user    0m0.747s
sys     0m0.000s

NDEBUG
real    0m0.239s
user    0m0.235s
sys     0m0.004s

force-inline can help a lot, but x3 slower than NDEBUG.

@ko1
Copy link
Contributor Author

ko1 commented May 21, 2020

I have implemented what I thought to be ideal. You don't like it. OK, then what do you think is ideal instead?

In other words, this patch disables newly introduce assertions into header files which have huge impact on performance.

It sounds too short-sighted. Incremental approach itself isn't bad, but needs a solid goal. Otherwise the implementation drifts away.

I think all assertions should be disabled on default, especially they are frequently used because it is difficult to estimate the performance impact.

  • runtime overhead (runtime cost)
  • performance tuning cost (development cost)
  • difficult to introduce more assertions because of tuning cost (development cost)

I didn't check existing assert() lines, so I can't say disabling them immediately is good idea, but it should be replaced later by checking (or asking the author, etc) them.

@nurse
Copy link
Member

nurse commented May 21, 2020

It sounds too short-sighted. Incremental approach itself isn't bad, but needs a solid goal. Otherwise the implementation drifts away.

If you want a solid goal, could you revert first and then discuss a solid goal for incremental development?

@ko1
Copy link
Contributor Author

ko1 commented May 21, 2020

I disagree with reverting all because the current code base is nice to improve IMO.

@shyouhei
Copy link
Member

The discussion so far...

  1. me: "Added assertions." Split ruby.h #2991
  2. people: "It's slow." [Feature #16837]
  3. me: "OK, let's flip the default." NDEBUG #3120
  4. ko1: "I think I have a better idea." Use RUBY_DEBUG instead of NDEBUG #3124
  5. me: "Great. But could show us your idea itself?"

@nurse I think what you need is #3120.

@shyouhei
Copy link
Member

@ko1 Thank you for the analysis. That explains the current situation very cleanly.

@ioquatix
Copy link
Member

ioquatix commented May 21, 2020

All assertions should be optional. Any non-optional assertion should be an exception or some other user facing guard, otherwise it could be a security issue. Enabling assertions per-file can be very useful for working on a specific piece of code. Sometimes in my c++ code, I have something like const bool DEBUG = true/false. Sometimes I add more fine grained logging.

@ko1
Copy link
Contributor Author

ko1 commented May 21, 2020

me: "Great. But could show us your idea itself?"

This PR is my idea. Any confusing?

@shyouhei
Copy link
Member

me: "Great. But could show us your idea itself?"

This PR is my idea. Any confusing?

What do you want to do withNDEBUG was unclear to me. Now you wrote in #3124 (comment) that you want to replace them. My concern was resolved.

@ko1
Copy link
Contributor Author

ko1 commented May 22, 2020

My concern was resolved.

Can I merge it?

Copy link
Member

@shyouhei shyouhei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, reviewed again and found no serious flaws.

@@ -95,7 +95,7 @@
#define RB_TYPE_P RB_TYPE_P
#define Check_Type Check_Type

#if RUBY_NDEBUG
#if !RUBY_DEBUG
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nitpick) If it was me I would avoid #if ! ... #else

@@ -33,11 +33,11 @@
# define RBIMPL_ATTR_PURE() /* void */
#endif

/** Enables #RBIMPL_ATTR_PURE iff. #RUBY_NDEBUG. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nitpick) This #FOO syntax is a Doxygen command. Do not delete the #.

@ko1
Copy link
Contributor Author

ko1 commented May 25, 2020

Thank you for comments. Could you fix them after I merged? (!#macro for doxygen?)

@ko1 ko1 merged commit 4ac4287 into ruby:master May 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants