Project

General

Profile

Actions

Bug #3141

closed

yield in an eigenclass definition

Added by mame (Yusuke Endoh) about 12 years ago. Updated about 11 years ago.

Status:
Closed
Priority:
Normal
Target version:
ruby -v:
ruby 1.9.2dev (2010-04-13 trunk 27329) [i686-linux]
Backport:
[ruby-dev:40975]

Description

=begin
ささださん
遠藤です。

#1018 に関連しますが、特異クラス定義の中で yield ができません。

class Object
def yield_eigenclass
class << self
yield self
end
end
end

Object.yield_eigenclass {|c| p c }

$ ruby19 t.rb
t.rb:4: Invalid yield
t.rb: compile error (SyntaxError)

調べていて知ったのですが、dfp[0] には

  • ISEQ_TYPE_METHOD, _CFUNC, _FINISH, _TOP の場合は rb_block_t*
  • それ以外の場合は前のフレームの dfp (vm_throw で使う)

が入っているんですね (なんという dirty hack ……) 。
vm_push_frame の 5 番目の引数 (specval) に渡される値はずっと謎
でした。

ISEQ_TYPE_CLASS の dfp[0] には前のフレームの dfp が入っています。
(ブロックへのポインタと区別できるよう、下から 2 ビット目を立てて
おくという、さらなる dirty hack (RUBY_VM_CLASS_SPECIAL_P) を見て
目がつぶれました)

一方で、ISEQ_TYPE_CLASS 以下の文脈からはブロックへの参照がない
状態になっていると思いました。ここまで、将来の自分のためのメモ。

そこで、ISEQ_TYPE_CLASS の dfp[0] にも rb_block_t* を入れることに
しませんか。

メリット:
- 上記のコードが動く
- RUBY_VM_CLASS_SPECIAL_P の dirty hack が不要になる

デメリット:
- vm_throw で分岐が増える場合がある

デメリットについては、break で以下を抜けるごとに 1 回分岐が増え
ます。

  • クラス定義 1.times { class C; break; end }
  • rescue 節 1.times { begin; ...; rescue; break; end }
  • ensure 節 1.times { begin; ...; ensure; break; end }

ここにクラス定義が来ることは非常にレアですし、rescue 節や ensure
節をネストすることもレアだと思うので、問題にならないんじゃない
かな、と思います。

というわけでパッチです。#1018 の wanabe さんのパッチも含みます。
make test-rubyspec と make check で特にエラーが増えない感じな
ことは確認しています。コミットしてもいいでしょうか。

diff --git a/compile.c b/compile.c
index 64411f9..7748fca 100644
--- a/compile.c
+++ b/compile.c
@@ -4229,7 +4229,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
rb_iseq_t *is = iseq;

if (is) {
  •  if (is->type == ISEQ_TYPE_TOP || is->type == ISEQ_TYPE_CLASS) {
    
  •  if (is->type == ISEQ_TYPE_TOP) {
     COMPILE_ERROR((ERROR_ARGS "Invalid return"));
     }
     else {
    

@@ -4269,7 +4269,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
unsigned long flag = 0;

INIT_ANCHOR(args);
  • if (iseq->type == ISEQ_TYPE_TOP || iseq->type == ISEQ_TYPE_CLASS) {
  • if (iseq->type == ISEQ_TYPE_TOP) {
    COMPILE_ERROR((ERROR_ARGS "Invalid yield"));
    }

diff --git a/insns.def b/insns.def
index 9541465..14485f2 100644
--- a/insns.def
+++ b/insns.def
@@ -953,7 +953,7 @@ defineclass

  /* enter scope */
  vm_push_frame(th, class_iseq,
  •    VM_FRAME_MAGIC_CLASS, klass, (VALUE) GET_DFP() | 0x02,
    
  •    VM_FRAME_MAGIC_CLASS, klass, (VALUE) GET_BLOCK_PTR(),
       class_iseq->iseq_encoded, GET_SP(), 0,
       class_iseq->local_size);
    
    RESTORE_REGS();
    diff --git a/proc.c b/proc.c
    index eff7a0b..5fa46f1 100644
    --- a/proc.c
    +++ b/proc.c
    @@ -374,16 +374,14 @@ proc_new(VALUE klass, int is_lambda)
    rb_control_frame_t *cfp = th->cfp;
    rb_block_t *block;
  • if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0 &&
  • !RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) {
  • if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0) {
block = GC_GUARDED_PTR_REF(cfp->lfp[0]);
  }
  else {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
  • if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0 &&
  •  !RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) {
    
  • if ((GC_GUARDED_PTR_REF(cfp->lfp[0])) != 0) {

    block = GC_GUARDED_PTR_REF(cfp->lfp[0]);
    

diff --git a/vm.c b/vm.c
index 678fcb8..e5de8ac 100644
--- a/vm.c
+++ b/vm.c
@@ -477,7 +477,6 @@ rb_vm_make_proc(rb_thread_t *th, const rb_block_t *block, VALUE klass)
}

  if (GC_GUARDED_PTR_REF(cfp->lfp[0])) {
  • if (!RUBY_VM_CLASS_SPECIAL_P(cfp->lfp[0])) {
    rb_proc_t *p;

    blockprocval = vm_make_proc_from_block(
    

@@ -486,7 +485,6 @@ rb_vm_make_proc(rb_thread_t *th, const rb_block_t *block, VALUE klass)
GetProcPtr(blockprocval, p);
*cfp->lfp = GC_GUARDED_PTR(&p->block);
}

  • }

    envval = rb_vm_make_env_object(th, cfp);

diff --git a/vm_core.h b/vm_core.h
index b5c1bf0..7ee1c1c 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -592,8 +592,6 @@ typedef rb_control_frame_t *
#define RUBY_VM_NORMAL_ISEQ_P(ptr)
(ptr && !RUBY_VM_IFUNC_P(ptr))

-#define RUBY_VM_CLASS_SPECIAL_P(ptr) (((VALUE)(ptr)) & 0x02)

#define RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp) ((rb_block_t *)(&(cfp)->self))
#define RUBY_VM_GET_CFP_FROM_BLOCK_PTR(b)
((rb_control_frame_t *)((VALUE *)(b) - 5))
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index cd74c8e..ae28ef3 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -890,8 +890,9 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n
const rb_block_t *block = GET_BLOCK_PTR();
rb_iseq_t *iseq;
int argc = (int)num;

  • int type = GET_ISEQ()->local_iseq->type;
  • if (GET_ISEQ()->local_iseq->type != ISEQ_TYPE_METHOD || block == 0) {
  • if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) {
    rb_vm_localjump_error("no block given (yield)", Qnil, 0);
    }
    iseq = block->iseq;
    @@ -1411,6 +1412,11 @@ vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,

    search_parent:
    

    if (cfp->iseq->type != ISEQ_TYPE_BLOCK) {

  •      if (cfp->iseq->type == ISEQ_TYPE_CLASS) {
    
  •  	cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
    
  •  	dfp = cfp->dfp;
    
  •  	goto search_parent;
    
  •      }
         dfp = GC_GUARDED_PTR_REF((VALUE *) *dfp);
         base_iseq = base_iseq->parent_iseq;
    

@@ -1476,10 +1482,17 @@ vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,
else if (state == TAG_RETURN) {
rb_control_frame_t *cfp = GET_CFP();
VALUE *dfp = GET_DFP();

  •  VALUE * const lfp = GET_LFP();
    
  •  VALUE *lfp = GET_LFP();
    
     /* check orphan and get dfp */
     while ((VALUE *) cfp < th->stack + th->stack_size) {
    
  •      if (!lfp) {
    
  •  	lfp = cfp->lfp;
    
  •      }
    
  •      if (cfp->dfp == lfp && cfp->iseq->type == ISEQ_TYPE_CLASS) {
    
  •  	lfp = 0;
    
  •      }
    
  •      if (cfp->lfp == lfp) {
     	if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA) {
     	    VALUE *tdfp = dfp;
    

--
Yusuke Endoh
=end


Related issues 1 (0 open1 closed)

Related to Ruby master - Bug #1018: return in an eigenclass definitionClosedko1 (Koichi Sasada)01/16/2009Actions
Actions #1

Updated by ko1 (Koichi Sasada) about 12 years ago

=begin
返事が遅くなってすみません.
これが良いと思います.よろしくお願いします.
=end

Actions #2

Updated by mame (Yusuke Endoh) about 12 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
This issue was solved with changeset r27714.
Yusuke, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

Actions

Also available in: Atom PDF