The Delphi Bug List

Entry No.
656
Compiler - Shouldn't compile
コンパイラはネストしたプロシージャ内への goto ステートメントによるジャンプを許してしまう。これは実行時に例外を発生させる
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
N/A Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists Exists
解説
Reported by Jordan Russell; checked by Reinier Sterkenburg
コードサンプル:
procedure TForm1.Button1Click(Sender: TObject);
label 1;
  procedure test;
  begin
    1: Beep;
  end;
begin
  goto 1;
end;

理想的には、これはコンパイルエラーになるべきです。なぜなら Delphi のヘルプには次のように書かれているからです:
ラベルの宣言,マークされた文,および goto 文は,同じブロックに存在していなければなりません(ブロックとスコープを参照)。このため,ジャンプによって手続きや関数の中に入ったり手続きや関数から出たりすることはできません。
(訳注: 日本語版 Delphi 5 のヘルプから原文に該当する部分を引用)
ユーザーからのコメント
Paul Farr
02 May 2001  05:11 AM GMT
最適化によって label ステートメントはローカルになっているので、上のステートメントは正当なものです。一貫性のためには次のコードも失敗するべきでしょう:
procedure TForm1.Button1Click(Sender: TObject);
  procedure test;
  label 1;
  begin
    1: Beep;
  end;
begin
  goto 1;
end;
            
Jordan Russell
02 May 2001  08:05 PM GMT
いいえ、その"goto"は、そのラベルがプロシージャ"test"の内部で宣言されているためにコンパイルを通らないのです。

このバグは、ラベルを宣言し、ネストしたプロシージャ内でそのラベルを使用し、そのプロシージャの外側からそのラベルへの goto によるジャンプをコンパイラが許してしまっていることです。
Sterling
06 Oct 2001  01:52 AM GMT
Borland は、本質的に goto のスコープをスタック内に制限しているのではないでしょうか?この場合、埋め込まれたプロシージャは新しいスタックフレームを生成しなので、この goto は正しいと思います(所有しているプロシージャの変数は埋め込まれたプロシージャでも有効であるという事実から、私はこの結論に達しました)。
Arsene von Wyss
09 Oct 2001  11:11 PM GMT
あなたの推測は正しくありません。ネストされたプロシージャでも、引数にレジスタが利用できない場合にはすぐに完全なスタックフレームが作成されることに注意してください。実際には、ローカルプロシージャは上位のプロシージャレベルの全ての変数にアクセス出来るように、上位のプロシージャの変数を指す隠されたポインタ引数を取得します。

もしスタックが使われないとしたら、プロシージャは呼び出し元に戻ったり、別のネストした(あるいはさらにネストした)プロシージャをコールしたり、再起呼び出しを行ったり出来ないでしょう。状況はとても明確です。goto は引数を持たずスタックに何も置きませんが、(ネストされているかどうかを問わず)プロシージの終了では常にスタックからなんらかの内容(少なくとも戻りアドレス)が pop されます。このため、これは本当にバグです。上のサンプルで、"Test"から"Sender"にアクセスしようとすると、不正なポインタを取得してしまうか、(SenderがNILなら)たいていはアクセス違反になってしまう事に注意してください。
Latest update of this entry: 2002-04-13
本家 The Delphi Bug List のエントリーはこちら
The Delphi Bug List 日本語訳 へ