Next: , Previous: , Up: Gforth locals   [Contents][Index]


6.22.1.2 Where are locals visible by name?

ローカル変数をその名前によって可視できるのはどこまででしょうか? – 基本的に、 その答えは、 ローカル変数がブロック構造言語で期待される場所で、 場合によってはもうちょっとだけ長くできます。 ローカル変数のスコープを制限したい場合は、 その定義を SCOPE...ENDSCOPE で囲んで下さい。

scope ( compilation  – scope ; run-time  –  ) gforth-0.2 “scope”
endscope ( compilation scope – ; run-time  –  ) gforth-0.2 “endscope”

これらのワードは制御構造のワードのように動作するため、 CS-PICK および CS-ROLL とともに使用して、 任意の方法で範囲を制限できます。

可視性の質問に対するより正確な答えが必要な場合のために、 ここで基本原則を示します: ローカル変数は、 ローカル変数の定義を通じてのみ到達できるすべての場所で可視です30。 つまり、 ローカル変数という定義を経由しないと到達できる場所では不可視です。 たとえば、 IF...ENDIF の中で定義されたローカル変数は ENDIF まで可視で、 BEGIN...UNTIL 内で定義されたローカル変数は UNTIL の後(たとえば、後続の ENDSCOPE まで)で可視です。

このソリューションの背景にある理由は次のとおりです: 私達は、 意味がある限り、 ローカル変数を可視させたいと考えています。 ユーザーは、 明示的なスコープを使用することで、 いつでも可視性を短くすることができます。 ローカル変数の定義によってのみ到達できる場所では、 ローカル変数名の意味は明らかです。 他の場所ではそうではありません。 ローカル変数定義が含まれていない制御フロー・パスでローカル変数はどのように初期化されるのでしょうか? 2 つの独立した制御フロー パスで同一ローカル変数名が 2 回定義されている場合、 それはどちらのローカル変数を意味するのでしょうか?

上記で、 ほぼすべてのユーザーにとって十分詳細であるため、 このセクションの残りの部分はスキップしてかまいません。 本当にすべての血みどろの詳細とオプションを知る必要がある場合は、 以下を読み続けてください。

このルールを実装するには、 コンパイラはどの場所が到達不能(unreachable)であるかを認識する必要があります。 AHEADAGAINEXITLEAVE の後で、 これが自動的に認識されます。 他の場合(例: ほとんどの THROW の後)、 UNREACHABLE というワードを使用して、 制御フローがその場所に到達しないことをコンパイラに伝えることができます。 UNREACHABLE が使用できる場所で使用されなかった場合、 唯一の結果は、 一部のローカル変数の可視性が上記のルールに記載されているよりも制限されることです。 UNREACHABLE を使用すべきではない場所で使用すると(つまり、 コンパイラに嘘をついた場合)、 バグのあるコードが生成されます。

UNREACHABLE ( ) gforth-0.2 “UNREACHABLE”

このルールのもう 1 つの問題は、 BEGIN で、 どのローカル変数が incoming back-edge で可視されるかをコンパイラが認識できないことです。 以下で説明するすべての問題は、 コンパイラのこの無知が原因です(BEGIN ループを例として使用してこの問題について説明します。 この説明は ?DO および他のループにも当てはまります)。 おそらく最も陰険な例は以下のとおりです:

AHEAD
BEGIN
  x
[ 1 CS-ROLL ] THEN
  {: x :}
  ...
UNTIL

これは、 可視性ルールに従って合法である必要があります。 x の使用には、 定義を介してのみ到達できます。 ただし、 下記に明示した使用法でなければなりません。

この例から、 可視性ルールを完全に実装するには大きな問題が伴うことが明らかです。 私たちの実装は、 一般的なケースを宣伝どおりに扱い、 例外は安全な方法で処理されます。 コンパイラは、 BEGIN の後に可視できるローカル変数について合理的な推測を行います。 悲観的すぎると、 ローカル変数が定義されていないという偽のエラーがユーザーに表示されます。 コンパイラが楽観的すぎる場合、 後でこれに気づき、 警告を発行します。 上記の場合、 コンパイラは x が使用時に未定義であることについて文句を言います。 このセクションのあいまいな例から、 コンパイラをトラブルに陥らせるには非常に特殊な制御構造が必要であることがわかりますが、 それでもコンパイラは多くの場合問題なく動作します。

BEGIN がそれより上から到達可能な場合、 最も楽観的な推測は、 BEGIN の前に可視であるすべてのローカル変数も BEGIN の後にも可視であることです。 この推測は、 BEGIN 経由でのみ入るすべてのループ、 特に通常の BEGIN...WHILE...REPEAT および BEGIN...UNTIL ループに対して有効であり、 コンパイラに実装されています。 BEGIN への分岐が AGAIN または UNTIL によって最終的に生成されると、 コンパイラは推測をチェックし、 それが楽観的すぎる場合はユーザーに警告します:

IF
  {: x :}
BEGIN
  \ x ? 
[ 1 cs-roll ] THEN
  ...
UNTIL

ここで、 xBEGIN までのみ存続しますが、 コンパイラは THEN まで存続すると楽観的に想定します。 UNTIL をコンパイルするときにこの違いに気づき、 警告を発行します。 ユーザーは警告を回避し、 明示的なスコープを使用して x が間違った領域で使用されていないことを確認できます:

IF
  SCOPE
  {: x :}
  ENDSCOPE
BEGIN
[ 1 cs-roll ] THEN
  ...
UNTIL

推測は楽観的であるため、 未定義のローカル変数に関する偽のエラー・メッセージは表示されません。

BEGIN がそれより上から到達可能でない場合(たとえば、 AHEAD または EXIT の後)、 BEGIN の後で定義されたローカル変数の可視については、 コンパイラは楽観的な推測を行うことさえできません。

悲観的に、 制御構造の外側の最新の場所(つまり、 制御フロー・スタック上に何もない場所)で可視であったすべてのローカル変数が可視であると仮定します。 これは以下のことを意味します:

: foo
  IF {: z :} THEN
  {: x :}
  AHEAD
    BEGIN
      ( * )
    [ 1 CS-ROLL ] THEN
    {: y :}
    ...
  UNTIL ;

ここで、 ( * ) でマークされた場所では、 x は可視ですが、 y は不可視です(ただし、 到達可能性ルールによれば、 可視であるべきです)。 z はそこでは不可視で、 可視であるべきではありません。

ただし、 ASSUME-LIVE を使用すると、 最上位の制御フロー・スタック項目が作成された時点と同じローカル変数が BEGIN で可視になるのだと、 コンパイラに想定させることができます。

ASSUME-LIVE ( orig – orig  ) gforth-0.2 “ASSUME-LIVE”

例えば、 以下のように使います

IF
  {: x :}
  AHEAD
    ASSUME-LIVE
    BEGIN
      x
    [ 1 CS-ROLL ] THEN
    ...
  UNTIL
THEN

ここで、 x のローカル変数定義は制御構造内にあるため、 x を使用した時点では x は可視ではありませんが、 ASSUME-LIVE を使用することで、 プログラマはコンパイラに AHEAD の時点で可視である、 そのローカル変数が、 BEGIN の時点でも可視であるべきであると伝えます。

BEGIN の前にローカル変数が定義されている他のケースは、 ASSUME-LIVE の前に適切な CS-ROLL を挿入する(そして ASSUME-LIVE の背後にある制御フロー・スタック操作を変更する)ことで処理できます。

ローカル変数が BEGIN の後で定義されている場合(ただし、 BEGIN の直後で可視である必要があります)は、 ループを再配置することによってのみ処理できます。 たとえば、上記の「最も陰険な」例は以下のように整理できます:

BEGIN
  {: x :}
  ... 0=
WHILE
  x
REPEAT

Footnotes

(30)

コンパイラ構築用語では「すべての場所はローカル変数の定義によって支配される」と言う


Next: How long do locals live?, Previous: Locals definitions words, Up: Gforth locals   [Contents][Index]