D 1.0   D 2.0
About Japanese Translation

Last update Sun Feb 6 11:48:08 2011

CやC++プログラマの方は、Dの文を非常に親しみやすく感じるでしょう。 いくつか面白い追加もあります。
Statement:
    ;
    NonEmptyStatement
    ScopeBlockStatement

NoScopeNonEmptyStatement:
    NonEmptyStatement
    BlockStatement

NoScopeStatement:
    ;
    NonEmptyStatement
    BlockStatement

NonEmptyOrScopeBlockStatement:
    NonEmptyStatement
    ScopeBlockStatement

NonEmptyStatement:
    NonEmptyStatementNoCaseNoDefault
    CaseStatement
    CaseRangeStatement
    DefaultStatement

NonEmptyStatementNoCaseNoDefault:
    LabeledStatement
    ExpressionStatement
    DeclarationStatement
    IfStatement
    WhileStatement
    DoStatement
    ForStatement
    ForeachStatement
    SwitchStatement
    FinalSwitchStatement
    ContinueStatement
    BreakStatement
    ReturnStatement
    GotoStatement
    WithStatement
    SynchronizedStatement
    TryStatement
    ScopeGuardStatement
    ThrowStatement
    AsmStatement
    PragmaStatement
    MixinStatement
    ForeachRangeStatement
    ConditionalStatement
    StaticAssert
     TemplateMixin

Statement の文法と Declaration の文法の間の曖昧性は、 すべて、Declaration を優先するという形で解決されます。 Statement として解釈させたい場合は、 括弧を使うことで Statement としてしか構文解析できないように書いて下さい。

スコープ文

ScopeStatement:
    NonEmptyStatement
    BlockStatement

文法定義でスコープ文(ScopeStatement)とされた箇所では NonEmptyStatementBlockStatement を書くことができ、ローカルシンボルのスコープが新しく導入されます。

新しいスコープが導入されるとは言っても、 ローカルシンボルの宣言が、同じ関数の他の宣言を隠す (shadowする) ことは許されていません。

void func1(int x)
{   int x;	// 不正。x は引数 x を隠す

    int y;

    { int y; }	// 不正。 y は外側のスコープの y を隠す

    void delegate() dg;
    dg = { int y; };	// ok。この y は同じ関数の中ではない

    struct S
    {
	int y;		// ok。この y はメンバ変数。ローカルシンボルでない
    }

    { int z; }
    { int z; }	// ok。この z は他の z を隠さない

    { int t; }
    { t++;   }	// 不正。t は未定義
}

これは、 複雑な関数で意図せず宣言が他の宣言を隠すことに起因するバグを避けるための仕様です。 ひとつの関数内では、ローカル名は重ならないようにすべきでしょう。

スコープブロック文

ScopeBlockStatement:
    BlockStatement

文法定義でスコープブロック文 (ScopeBlockStatement) とされた箇所には BlockStatement のみが記述でき、新しいスコープが導入されます。

ラベル付き文

文にはラベルを付けられます。 ラベルとは、文の前に置く識別子です。

LabeledStatement:
    Identifier : NoScopeStatement

空文を含めて任意の文がラベル付け可能で、 goto文の飛び先とできます。 continue 文や break 文の飛び先ともなり得ます。

ラベルの名前空間は、 宣言や変数、型などとは独立しています。 しかしそれにもかかわらず、ローカルの宣言と同じ名前のラベルは使用できません。 ラベル名の有効範囲は、そのラベルのある関数本体のです。 名前空間のネストは行われません。つまり、 ブロックの中のラベルは、外からでもアクセスできます。

ブロック文

BlockStatement:
    { }
    { StatementList }

StatementList:
    Statement
    Statement StatementList

ブロック文とは、{ } で囲まれた文の並びのことです。 テキスト上での順序と同じ順で、各文が実行されます。

式文

ExpressionStatement:
    Expression ;
式が評価されます。

副作用のない式、 例えば (x + x)、 は式文としては不正です。 そのような文が必要な場合は、 voidへとキャストすることで正当なコードになります。

int x;
x++;                // ok
x;                  // 不正
1+1;                // 不正
cast(void)(x + x);  // ok

宣言文

変数や型を宣言します。
DeclarationStatement:
    Declaration

宣言文の例です:

int a;		 // a を 型 int と宣言し、0 に初期化
struct S { }	 // 構造体 s の宣言
alias int myint;

If 文

if文では、文を条件付きで実行します。
IfStatement:
	if ( IfCondition ) ThenStatement
	if ( IfCondition ) ThenStatement else ElseStatement

IfCondition:
	Expression
	auto Identifier = Expression
	BasicType Declarator = Expression

ThenStatement:
	ScopeStatement

ElseStatement:
	ScopeStatement
まず Expression が評価されます。この結果は、boolean であるかまたはbooleanに変換可能な型です。 結果がtrueならばThenStatementが実行され、 そうでなければElseStatementが実行されます。

'ぶら下がりelse' の問題については、elseは一番近いifと結合する、 と定められています。

auto Identifier を記述した場合、 その名前の変数が宣言されて、 Expression の型と値が割り当てられてます。変数のスコープは、 初期化時から ThenStatement の終了時までです。

Declarator を記述した場合、 その名前の変数が宣言されて、 Expression の型と値が割り当てられてます。変数のスコープは、 初期化時から ThenStatement の終了時までです。

import std.regexp;
...
if (auto m = std.regexp.search("abcdef", "b(c)d"))
{
    writefln("[%s]", m.pre);      // [a] を表示
    writefln("[%s]", m.post);     // [ef] を表示
    writefln("[%s]", m.match(0)); // [bcd] を表示
    writefln("[%s]", m.match(1)); // [c] を表示
    writefln("[%s]", m.match(2)); // [] を表示
}
else
{
    writefln(m.post);    // エラー。m は未定義
}
writefln(m.pre);         // エラー。m は未定義

While 文

WhileStatement:
    while ( Expression ) ScopeStatement
while文は簡単なループを構成します。 まず Expression が評価されます。 この結果は、boolean であるかまたはbooleanに変換可能な型です。 結果がtrueならば内容文が実行され、 もう一度 Expression の評価へ戻ります。 このループは Expression がfalseへ評価されるまで続きます。
int i = 0;
while (i < 10)
{
    foo(i);
    i++;
}
break文はループを終了します。 continue文は、 直ちに Expression の評価へ処理を飛ばします。

Do-While 文

DoStatement:
    do ScopeStatement while ( Expression )
do-while文は簡単なループを構成します。 ScopeStatement が実行されます。 その次に、boolean であるかまたはbooleanに変換可能な型をもつ Expression が評価されます。 trueならばまたループを繰り返します。 Expression がfalseへ評価されるまでループが続きます。
int i = 0;
do
{
    foo(i);
} while (++i < 10);
break文はループを終了します。 continue文は、 直ちに Expression の評価へ処理を飛ばします。

For 文

for文は、 initialization, test, increment の3つの節を持つループです。
ForStatement:
	for (Initialize Testopt ; Incrementopt) ScopeStatement

Initialize:
	;
	NoScopeNonEmptyStatement

Test:
	Expression

Increment:
	Expression

まず Initializer が実行されます。 その次に、boolean であるかまたはbooleanに変換可能な型をもつ Test が 評価されます。これがtrueなら内容文が実行されます。その後、 Increment が実行され、次にまた Test の評価に行きます。 これがtrueならばまたループが繰り返されます。 Test がfalseになるまでこのループが続きます。

break文はループを終了します。 continue文は、 直ちに Increment の評価へ処理を飛ばします。

Initializer で変数が宣言されていれば、その変数のスコープは for文の本体の最後まで拡大されます。例:

for (int i = 0; i < 10; i++)
	foo(i);
これは次と同等です。
{   int i;
    for (i = 0; i < 10; i++)
	foo(i);
}
ループの中身を空にはできません:
for (int i = 0; i < 10; i++)
	;	// 不正
代わりに次のように書きます:
for (int i = 0; i < 10; i++)
{
}
Initializer は省略可能です。Test も省略可能で、 その場合あたかも常にtrueと評価されるかのように扱われます。

Foreach 文

foreach文では、集成体の内容をループします。
ForeachStatement:
    Foreach (ForeachTypeList ; Aggregate) NoScopeNonEmptyStatement

Foreach:
    foreach
    foreach_reverse

ForeachTypeList:
    ForeachType
    ForeachType , ForeachTypeList

ForeachType:
    ref BasicType Declarator
    BasicType Declarator
    ref Identifier
    Identifier

Aggregate:
    Expression

Aggregate がまず評価されます。 その結果は、 静的配列, 動的配列, 連想配列, 構造体, クラス, デリゲート, タプル のいずれかの型を持つ式でなければなりません。次に、 集成体の各要素につき一度ずつ、NoScopeNonEmptyStatement が実行されます。 毎回、 ForeachTypeList で宣言した変数にその集成体の要素がコピーされます。 変数が ref だった場合、参照となります。

集成体はループ中は不変でなければなりません。つまり、 ループ本体の NoScopeNonEmptyStatement で要素を追加したり削除することは禁止されています。

配列に対する foreach

式部分が静的/動的配列な場合、 1つか2つの変数を宣言することが可能です。 変数が1つの時は、 その変数は value と呼ばれ、 配列の要素が一つずつ格納されます。 変数の型は、下で示す特別な場合を除き、 配列の内容と合致しなければなりません。 2つの変数が宣言されていると、一つ目がindex、二つ目がvalueと呼ばれます。index変数はintuint、またはsize_t型とし、 ref指定はできません。 配列要素のインデックスが格納されます。

char[] a;
...
foreach (int i, char c; a)
{
    writefln("a[%d] = '%c'", i, c);
}

foreach では、 配列の要素は0番目から始まって 最大の添字を持つ要素で終わる順番で処理されます。 foreach_reverse では、 配列要素が逆順で渡されます。

文字の配列に対する foreach

式部分が char または wchar あるいは dchar の静的/動的配列である場合、value の型は char, wchar, dchar のいずれにもできます。 これを用いることで、 どの種類のユニコード文字配列も、 任意の種類のユニコード文字型へとデコードすることができます:

char[] a = "\xE2\x89\xA0";	// \u2260 を UTF-8 の3バイトで表現

foreach (dchar c; a)
{
    writefln("a[] = %x", c);	// 'a[] = 2260' を表示
}

dchar[] b = "\u2260";

foreach (char c; b)
{
    writef("%x, ", c);	// 'e2, 89, a0' を表示
}

集成体が文字列リテラルのときは、 char, wchar, dchar 配列としてアクセス可能です:

void test()
{
    foreach (char c; "ab")
    {
	writefln("'%s'", c);
    }
    foreach (wchar w; "xy")
    {
	writefln("'%s'", w);
    }
}

出力は次のようになります:

'a'
'b'
'x'
'y'

連想配列に対する foreach

式部分が連想配列な場合、 1つか2つの変数を宣言することが可能です。 変数が1つの時は、 その変数は value と呼ばれ、 配列の要素が一つずつ格納されます。 変数の型は配列の内容と合致しなければなりません。 2つの変数が宣言されていると、 一つ目がindex、二つ目がvalueと呼ばれます。index変数の型は連想配列のインデックス型と合致させます。 ref指定はできません。配列要素のインデックスが格納されます。 foreach で要素が列挙される順番は決まっていません。 また、連想配列に対する foreach_reverse は不正です。

double[char[]] a;	// index型は char[], value型は double
...
foreach (char[] s, double d; a)
{
    writefln("a['%s'] = %g", s, d);
}

Rangeを表す構造体やクラスに対する foreach

以下のプロパティの定義された構造体やクラスは Range と呼ばれ、foreachでループすることが可能です:

Range のプロパティ
名前 用途
.empty もう要素がない時に true を返す
.popFront Range の左端をひとつ右に動かす
.popBack Range の右端をひとつ左に動かす
.front Range の最左端の要素を返す
.back Range の最右端の要素を返す

つまり:

foreach (e; range) { ... }

このコードは以下のように変形されます:

for (auto __r = range; !__r.empty; __r.next)
{   auto e = __r.head;
   ...
}

同様に:

foreach_reverse (e; range) { ... }

このコードは以下のように変形されます:

for (auto __r = range; !__r.empty; __r.retreat)
{   auto e = __r.toe;
   ...
}

Range としてのプロパティが存在しない場合は、代わりに opApply メソッドが使用されます。

opApply を持つ構造体やクラスに対する foreach

構造体やクラスオブジェクトについては、foreach の順序は特別な opApply メンバ関数で定義されます。 foreach_reverse の動作は、 opApplyReverse メンバ関数の定義によります。 対応する foreach 文を使用するためには、 型ごとにこれらの特別な関数をユーザー定義する必要があります。 これらの関数の型は次の通りです:

int opApply(int delegate(ref Type [, ...]) dg);

int opApplyReverse(int delegate(ref Type [, ...]) dg);

Type は foreach で Identifier の宣言に使われた Type と一致するものです。複数の ForeachType は、 opApplyopApplyReverse へ渡されるdelegateの複数の Type に対応します。 複数個の opApply が存在してもかまいません。 適切な物が、 foreach文の ForeachTypedg の型が合うように選択されます。関数の本体では、 そのオブジェクト内の要素を順にたどり、 それぞれを dg へ渡します。dg が 0 を返せば、 処理を次の要素へと進めます。dg が 0以外の値を返すと、 繰り返しを止めてその値をreturnする必要があります。 それ以外で、全ての要素をたどり終わったならば、 最後に0をreturnします。

例えば、二つの要素を含むコンテナクラスを考えてみましょう:

class Foo
{
    uint array[2];

    int opApply(int delegate(ref uint) dg)
    {   int result = 0;

	for (int i = 0; i < array.length; i++)
	{
	    result = dg(array[i]);
	    if (result)
		break;
	}
	return result;
    }
}
これを使った例は次のようになるでしょう:
void test()
{
    Foo a = new Foo();

    a.array[0] = 73;
    a.array[1] = 82;

    foreach (uint u; a)
    {
	writefln("%d", u);
    }
}
出力はこうです:
73
82

デリゲートに対する foreach

Aggregate には、 opApply と同じ型のデリゲートを指定することも可能です。これによって、 一つのクラスや構造体に 多くのループ方法を名前付きで共存させることができます。

タプルに対する foreach

集成体としてタプルを指定した場合は、 一つか二つの変数を宣言できます。一つの場合、 その値にはタプルの要素が一つずつセットされます。 変数の型を指定した場合、 タプルの中身と合う型でなければなりません。 型が指定されていないときは、 変数の型はタプルの要素の型になり、繰り返し毎に変化します。 二つ変数を宣言した場合、 一つめが index、 二つめが value となります。indexint 型か uint 型のどちらかで、ref にはできません。 index にはタプルのいくつ目の要素であるかが格納されます。

タプルが型のリストであった場合、 foreach文はそれぞれの型に対して一度ずつ実行され、 変数はその型へのaliasとなります。

import std.stdio;
import std.typetuple;	// TypeTupleテンプレート

void main()
{
    alias TypeTuple!(int, long, double) TL;

    foreach (T; TL)
    {
	writefln(typeid(T));
    }
}

この出力は:

int
long
double

foreach の ref パラメータ

元の要素を書き換えるためには、ref が使えます。

void test()
{
    static uint[2] a = [7, 8];

    foreach (ref uint u; a)
    {
	u++;
    }
    foreach (uint u; a)
    {
	writefln("%d", u);
    }
}
出力:
8
9

ref はインデックス値には適用できません。

ForeachType に型指定がなかった場合は、 Aggregate の型から自動で推論されます。

foreach の制限事項

foreachの繰り返しの最中に、 集成体自身をサイズ変更や再割り当て、 解放や再代入、破棄などすることはできません。

int[] a;
int[] b;
foreach (int i; a)
{
    a = null;			// エラー
    a.length = a.length + 10;	// エラー
    a = b;			// エラー
}
a = null;			// ok

foreachの本体部分での break 文はループを終了します。continue 文は、 直ちに次の Expression の評価へ処理を飛ばします。

Switch 文

switch文は、 式の値に応じて case文のうちの一つへ処理を移します。
SwitchStatement:
	switch ( Expression ) ScopeStatement

CaseStatement:
	case ArgumentList : ScopeStatementList

CaseRangeStatement:
	case FirstExp : .. case LastExp : ScopeStatementList

FirstExp:
	AssignExpression

LastExp:
	AssignExpression

DefaultStatement:
	default : ScopeStatementList

ScopeStatementList:
	StatementListNoCaseNoDefault

StatementListNoCaseNoDefault:
	StatementNoCaseNoDefault
	StatementNoCaseNoDefault StatementListNoCaseNoDefault

StatementNoCaseNoDefault:
    ;
    NonEmptyStatementNoCaseNoDefault
    ScopeBlockStatement

Expression が評価されます。 結果の型 T は整数型か、 char[]、wchar[]、または dchar[] のいずれかです。結果はcaseの式の各々と 比較され、マッチしたcase文へ処理が移ります。

caseのArgumentListとは、 コンマで区切られた式のリストです。

CaseRangeStatement は、 FirstExp から LastExp までの case ラベルを並べるための省略記法です

どのcaseの式ともマッチしなかった場合、default文があれば、 処理はdefault文へ移ります。

更にdefault文もなかった場合は、 std.switcherr.SwitchError例外が送出されます。 これは、 enum に新しい値を追加したときにswitch文を変更し忘れる、 などのよくあるバグを早く見つけられるようにするためです。 これはC/C++とは異なった動作です。

caseの式は、定数値か配列、または実行時初期化された 整数型の const/immutable 変数でなければなりません。 また、switch の Expression の型へと暗黙変換可能である必要があります。

case の式は全て異なる値へと評価される必要があります。 const/immutable 変数には必ず全て違う変数を指定します。 値が同じだった場合、 その値をもつ最初のcase節に制御が渡ります。

二個以上の default 文を書くのは不正です。

ScopeStatementList は新しいスコープを作ります。

switch文に関連付けられたcase文やdefault文は、 ネストしたブロックの中にあっても構いません。 例えば次は許されます:

    switch (i)
    {
	case 1:
	{
	    case 2:
	}
	    break;
    }

case文は続くcase文へと 'fall through' します。 break 文があれば、switch の BlockStatement を終了します。 例えば:

switch (i)
{
    case 1:
	x = 3;
    case 2:
	x = 4;
	break;

    case 3,4,5:
	x = 5;
	break;
}

i が 1 ならば x は 4 になります。

文字列もswitch文で使用できます。例えば:

char[] name;
...
switch (name)
{
    case "fred":
    case "sally":
	...
}

コマンドラインスイッチの処理などへの応用では、 この構文によって、直接的でエラーの少ないコードが書けるようになります。 char, wchar, dchar 文字列のいずれも使用できます。

実装ノート: コンパイラのコード生成器は、 caseは実行頻度の高い順に並べられていると仮定して構いません。 プログラムの正しさの面ではこの仮定は無関係ですが、 パフォーマンスの面を考えると 意味があります。

Final Switch 文

FinalSwitchStatement:
	final switch ( Expression ) ScopeStatement

final switch 文は基本的には switch 文ですが、 以下の違いがあります:

Continue 文

ContinueStatement:
    continue;
    continue Identifier ;
continue文はループの現在の処理を中断し、 ループの先頭へ処理を戻します。 continueは、最内周のwhile,for,foreach,doループにはたらきます。 for文のincrement節は実行されます。

continue の後ろに Identifier が続いている時は、 その Identifier はcontinue文を囲うループのラベルでなければならず、 continue文によってそのループの先頭へ処理を戻します。 そのラベルの付いたループが存在しなければエラーです。

関連するfinally節は全て実行され、 同期オブジェクトは解放されます。

注意: finally節でreturn,throw文やfinallyの外へのgoto文が実行された場合、 continueの目的地へは処理は到達しません。

for (i = 0; i < 10; i++)
{
    if (foo(i))
	continue;
    bar();
}

Break 文

BreakStatement:
    break;
    break Identifier ;
break文は、そのbreak文を含む文を終了します。 breakは最内周のwhile,for,foreach,do,switch文を終了させ、 それらの次にある文へ処理を移します。

break の後ろに Identifier が続いている時は、 その Identifier はbreak文を囲うループやswitchのラベルでなければならず、 break文によってそのループ等を終了します。 そのラベルの付いたループ等が存在しなければエラーです。

関連するfinally節は全て実行され、 同期オブジェクトは解放されます。

注意: finally節でreturn,throw文やfinallyの外へのgoto文が実行された場合、 breakの目的地へは処理は到達しません。

for (i = 0; i < 10; i++)
{
    if (foo(i))
	break;
}

Return 文

ReturnStatement:
    return;
    return Expression ;
返値を定めて、 現在の関数を終了します。 void以外の返値型を持つ関数ならば、 Expression が必要です。 Expression は暗黙の内に返値型へ変換されます。

void以外の返値型を持つ関数では、インラインアセンブラを含まない場合、 最低でも1つのreturn, throw, あるいは assert(0) 文が必要です。

voidが返値型の関数であっても、return に Expression を指定することは可能です。この場合、Expression は評価されますが、何か返値を返すようになるわけではありません。 Expression が副作用を持たず、 返値型が void であった場合、これは不正なコードとなります。

関数が完全にreturnする前に、 scope記憶クラスを持つオブジェクトが破棄され、 finally節は実行され、 scope(exit) 文が実行され、 scope(success) 文も実行され、 同期オブジェクトは 解放されます。

finally節でfinally節を抜けるgoto文やthrow文、return文が実行された場合、 関数からは戻りません。

out事後条件 ("契約プログラミング"参照) がある場合は、 Expression の評価後、 関数が処理を戻す前にout節が実行されます。

int foo(int x)
{
    return x + 3;
}

Goto 文

GotoStatement:
    goto Identifier ;
    goto default ;
    goto case ;
    goto case Expression ;
ラベル Identifier の付いた文へ処理を移します。
    if (foo)
	goto L1;
    x = 3;
L1:
    x++;
二番目の形 goto default; では、 このgoto文を囲む最も内側の SwitchStatementDefaultStatement へ処理を移します。

三番目の形 goto case; では、 このgoto文を囲む最も内側のSwitchStatementにおける、 直後の CaseStatement へ処理を移します。

四番目の形 goto case Expression; では、 このgoto文を囲む最も内側のSwitchStatementにおける、 Expression とマッチするCaseStatementへと処理を移します。

switch (x)
{
    case 3:
	goto case;
    case 4:
	goto default;
    case 5:
	goto case 4;
    default:
	x = 4;
	break;
}
関連するfinally節は全て実行され、 同期オブジェクトは解放されます。

初期化を skip するようなgotoは不正です。

With 文

with文によって、 同じオブジェクトへの参照の繰り返しを簡単化できます。
WithStatement:
	with ( Expression ) ScopeStatement
	with ( Symbol ) ScopeStatement
	with ( TemplateInstance ) ScopeStatement
Expression はクラスの参照か、 構造体のインスタンスへと評価されるものを指定します。 WithStatement の本体では、シンボルを探す名前空間として、 まずそのオブジェクトへの参照が用いられるようになります。次のようなwith文
with (expression)
{
    ...
    ident;
}
は、意味的に次と同値です:
{
    Object tmp;
    tmp = expression;
    ...
    tmp.ident;
}

Expression は一度しか実行されません。 with文では、 thissuper の指す対象は変わりません。

Symbol には、スコープかまたは TemplateInstance を指定し、 対応するスコープからシンボルが検索されるようになります。 例えば:

struct Foo
{
    alias int Y;
}
...
Y y;		// エラー、Y が未定義
with (Foo)
{
    Y y;	// Foo.Y y; と同じ
}

ローカルシンボルを同じ識別子で隠してしまうような、 with 式の使い方は禁止されています。 これは、オブジェクトの宣言に新しいメンバが追加された場合に、 意図せずコードの動作を変えてしまうような危険を避けるためです。

struct S
{
    float x;
}

void main()
{
    int x;
    S s;
    with (s)
    {
        x++;  // エラー。x の宣言を隠している
    }
}

Synchronize 文

synchronize文は、クリティカルセクションで他の文を包んで、 複数のスレッドが同時に文を実行することを禁止します。

SynchronizedStatement:
    synchronized ScopeStatement
    synchronized ( Expression ) ScopeStatement

同時に高々1つのスレッドのみが ScopeStatement を実行することが許されます。

どの mutex によってこの動作が実現されるかは、Expression によって決まります。Expression が指定されなかった場合、 一つのsynchronized文につき一つグローバルなmutexが作成されます。 異なるsynchronized文は異なるグローバルmutexを使用します。

Expression がある場合は、 オブジェクトへの参照か、 Interfaceの場合はInterfaceを実装するオブジェクトへとキャストで評価されます。 使用される mutex はそのオブジェクトのインスタンス毎に用意されるもので、 同じインスタンスを参照する全てのsynchronized文で共有されます。

ScopeStatement が例外,goto,returnなどで終了した場合であっても、 ロックは解除されます。

例:

synchronized { ... }

これは標準的なクリティカルセクションの実装です。

Try Statement

例外処理は、try-catch-finally 文で行われます。
TryStatement:
	try ScopeStatement Catches
	try ScopeStatement Catches FinallyStatement
	try ScopeStatement FinallyStatement

Catches:
	LastCatch
	Catch
	Catch Catches

LastCatch:
	catch NoScopeNonEmptyStatement

Catch:
	catch ( CatchParameter ) NoScopeNonEmptyStatement

CatchParameter:
	BasicType Identifier

FinallyStatement:
	finally NoScopeNonEmptyStatement

CatchParameter は型Tの変数vを宣言します。 型TはThrowableまたはその派生クラスです。 型Tの派生クラスがthrowされた場合、 そのオブジェクトで v が初期化され、 catch文の中身が実行されます。

型Tだけが書かれ変数名vが無いときも、 catch文は実行されます。

もし CatchParameter の型T1が後ろに続く 型T2の Catch を隠していた場合、つまりT1がT2と同じ型か T2の基底クラスの場合は、エラーになります。

LastCatch は全ての例外をキャッチします。

FinallyStatement は、 try ScopeStatement がgoto,break,continue,return,例外, 正常終了のいずれで終わる場合も必ず実行されます。

例外が FinallyStatement の中で発生し、元の例外が catch されるより前に catch されなかった場合、Throwablenext メンバによって前の例外にチェインされます。 注意点として、他の多くのプログラミング言語と違って、 D では新しい例外が古い例外を置き換えることはありません。 代わりに、新しい例外は最初のれいがいによって引き起こされた '連鎖ダメージ' と見なされます。 キャッチすべきは元々の例外の方で、そちらをキャッチすると、 連鎖した例外もそこから取得できます。

Error から派生したオブジェクトがthrowされたときの扱いは異なります。 これらの例外は通常のチェインメカニズムを迂回し、 最初の Error のみがcatch可能となるように処理されます。 それ以降に発生した例外のリストに加え、Error には、迂回が起こったときには、 一番最初に発生した元々の例外へのポインタも含まれます。 これによって、例外階層全体が手に入ることになります。

import std.stdio;

int main()
{
    try
    {
	try
	{
	    throw new Exception("first");
	}
	finally
	{
	    writefln("finally");
	    throw new Exception("second");
	}
    }
    catch(Exception e)
    {
	writefln("catch %s", e.msg);
    }
    writefln("done");
    return 0;
}
これは次のような表示になります:
finally
catch first
done

FinallyStatement から goto, break, continue, return などで脱出したり、gotoで中に飛び込んだりしてはいけません。

FinallyStatement の中には Catches を含むことはできません。 この制約は将来的には緩和される予定です。

Throw 文

例外を投げます。
ThrowStatement:
	throw Expression ;
Expression は Throwable 型の参照へと評価される必要があります。そのオブジェクトが例外として送出されます。
throw new Exception("message");

スコープガード文

ScopeGuardStatement:
	scope(exit) NonEmptyOrScopeBlockStatement
	scope(success) NonEmptyOrScopeBlockStatement
	scope(failure) NonEmptyOrScopeBlockStatement
ScopeGuardStatement は、指定された NonEmptyOrScopeBlockStatement を、 この文自体が現れた箇所ではなく、 現在のスコープの終了時に実行します。 scope(exit) は、 スコープから通常終了するときと例外で抜ける時の両方でNonEmptyOrScopeBlockStatementを実行します。 scope(failure) は、 スコープを例外で抜ける時のみNonEmptyOrScopeBlockStatementを実行します。 scope(success) は、 スコープから通常終了するときのみNonEmptyOrScopeBlockStatementを実行します。

一つのスコープに複数の ScopeGuardStatement があった場合は、 ソースコード文字列上で現れた順番の逆順で実行されます。 そのスコープ終了時に破棄されるscopeインスタンスが存在する場合も、 それらのデストラクタも、ソースコード上の逆順で ScopeStatement の間に挟まって実行されます。

writef("1");
{
    writef("2");
    scope(exit) writef("3");
    scope(exit) writef("4");
    writef("5");
}
writefln();
というプログラムは以下を出力します:
12543
{
    scope(exit) writef("1");
    scope(success) writef("2");
    scope(exit) writef("3");
    scope(success) writef("4");
}
writefln();
の場合はこうなります:
4321
class Foo
{
    this() { writef("0"); }
    ~this() { writef("1"); }
}

try
{
    scope(exit) writef("2");
    scope(success) writef("3");
    scope Foo f = new Foo();
    scope(failure) writef("4");
    throw new Exception("msg");
    scope(exit) writef("5");
    scope(success) writef("6");
    scope(failure) writef("7");
}
catch (Exception e)
{
}
writefln();
の時は:
0412
scope(exit)scope(success) 文は、 throw, goto, break, continue, return 文で終了したり、 goto で中に飛び込んだりしてはいけません。

Asm 文

インラインアセンブラはasm文によってサポートされます。
AsmStatement:
	asm { }
	asm { AsmInstructionList }

AsmInstructionList:
	AsmInstruction ;
	AsmInstruction ; AsmInstructionList
asm文は、アセンブラ言語の命令を直接使うことを可能にします。 これによって、外部のアセンブラを用いずにCPUの特殊命令の 実行などが簡単に実現できます。 関数の呼び出し規約やスタックの処理などは、 Dコンパイラが行います。

命令のフォーマットは、 CPUの命令セットに大きく依存しますので、 実装ごとに定義されています。 しかし、フォーマットは次の規約に従っています:

これらのルールは、Dのソースコードが構文解析,意味解析と独立に 確実にトークン分割できるために定められています。

例えばIntelプラットホームでは:

int x = 3;
asm
{
    mov	EAX,x;		// xをロードしてEAXレジスタに入れる
}
インラインアセンブラによってハードウェアの直接制御も可能です:
int gethardware()
{
    asm
    {
	    mov	EAX, dword ptr 0x1234;
    }
}
Dの実装によっては、例えばDからCへのトランスレータなどでは、 インラインアセンブラは意味をなしません。この場合は、 インラインアセンブラが実装されている必要があります。これを確かめるには、version文を使うことができます:
version (D_InlineAsm_X86)
{
    asm
    {
	...
    }
}
else
{
    /* ... なんらかのworkaround ... */
}

意味的に連続している複数の AsmStatement 文の間には、 余計な命令 (レジスタの退避や復元等)がコンパイラによって勝手に挿入されることはありません。

pragma文

PragmaStatement:
    Pragma NoScopeStatement

mixin文

MixinStatement:
    mixin ( AssignExpression ) ;

AssignExpression は、 コンパイル時定数へと評価される文字列である必要があります。 Tその文字列の内容は正当な StatementList としてコンパイルできるものでなければならず、その場合、その通りにコンパイルされます。

import std.stdio;

void main()
{
    int j;
    mixin("
	int x = 3;
	for (int i = 0; i < 3; i++)
	    writefln(x + i, ++j);
	");    // ok

    const char[] s = "int y;";
    mixin(s);  // ok
    y = 4;     // ok, mixin が y を宣言している

    char[] t = "y = 3;";
    mixin(t);  // エラー。t はコンパイル時に評価できない

    mixin("y =") 4; // エラー。文字列は完全な文でなければならない

    mixin("y =" ~ "4;");  // ok
}

Foreach Range 文

foreach range 文は、指定された範囲をループします。
ForeachRangeStatement:
    Foreach (ForeachType ; LwrExpression .. UprExpression ) ScopeStatement

LwrExpression:
    Expression

UprExpression:
    Expression

ForeachType では、型指定つきもしくは型指定なしで変数を宣言します。 型指定が無い場合は LwrExpressionUprExpression から推論されます。 ScopeStatementn 回( n = UprExpression - LwrExpression ) 実行されます。 UprExpressionLwrExpression 以下だった場合は、 ScopeStatement は1度も実行されません。 Foreachforeach の時は、変数は最初 LwrExpression に設定され、繰り返しのたびに増加していきます。 Foreachforeach_reverse の時は、変数は最初 UprExpression に設定され、繰り返しのたびに減少していきます。 LwrExpressionUprExpression は、 ScopeStatement の実行回数とは無関係に、 必ずちょうど1度ずつ実行されます。

import std.stdio;

int foo()
{
    writefln("foo");
    return 10;
}

void main()
{
    foreach (i; 0 .. foo())
    {
	writef(i);
    }
}

出力:

foo0123456789