Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

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
    ImportDeclaration

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 iを記述した場合、 その名前の変数が宣言されて、 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ならば ScopeStatement が実行され、 もう一度 Expression の評価へ戻ります。 このループは Expression がfalseへ評価されるまで続きます。
int i = 0;
while (i < 10) {
  foo(i);
  i++;
}
BreakStatement はループを終了します。 ContinueStatement は、 直ちに Expression の評価へ処理を飛ばします。

do 文

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

for 文

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

Initialize:
    ;
    NoScopeNonEmptyStatement

Test:
    Expression

Increment:
    Expression

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

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

ForStatement は新しいスコープを導入します。 Initialize で変数が宣言されていれば、その変数のスコープは 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++)
{
}
Initialize は省略可能です。 Test も省略可能で、 その場合あたかも常にtrueと評価されるかのように扱われます。

foreach 文

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

Foreach:
    foreach
    foreach_reverse

ForeachTypeList:
    ForeachType
    ForeachType , ForeachTypeList

ForeachType:
    refopt BasicType Declarator
    refopt 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".dup;  // \u2260 を UTF-8 の3バイトで表現

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

dchar[] b = "\u2260"d.dup;

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[string] a; // index 型は string, value 型は double
...
foreach (string s, double d; a)
{
  writefln("a['%s'] = %g", s, d);
}

レンジを表す構造体やクラスに対する foreach

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

レンジのプロパティ
プロパティ 用途
.empty もう要素がない時に true を返す
.front レンジの最左端の要素を返す
.back レンジの最右端の要素を返す
foreach でのレンジの操作
メソッド 用途
.popFront() レンジの左端をひとつ右に動かす
.popBack() レンジの右端をひとつ左に動かす

つまり:

foreach (e; range) { ... }

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

for (auto __r = range; !__r.empty; __r.popFront())
{
  auto e = __r.front;
  ...
}

同様に:

foreach_reverse (e; range) { ... }

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

for (auto __r = range; !__r.empty; __r.popBack())
{
  auto e = __r.back;
  ...
}

レンジとしてのプロパティが存在しない場合は、代わりに 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 に対応します。 複数個の opApplyopApplyReverse が存在してもかまいません。 適切な物が、 ForeachStatementForeachTypedg の型が合うように選択されます。関数の本体では、 そのオブジェクト内の要素を順にたどり、 それぞれを dg へ渡します。dg が 0 を返せば、 処理を次の要素へと進めます。 dg が 0以外の値を返すと、 繰り返しを止めてその値をreturnする必要があります。 それ以外で、全ての要素をたどり終わったならば、最後に0をreturnします。

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

class Foo {
  uint[2] array;

  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 にはできません。 タプルのいくつ目の要素であるかが格納されます。

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

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

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

  foreach (T; TL)
  {
    writeln(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 += 10; // エラー
  a = b;          // エラー
}
a = null;         // ok

foreach range 文

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

LwrExpression:
    Expression

UprExpression:
    Expression

ForeachType では変数を明示的な型指定付きで宣言するか、 LwrExpressionUprExpression から暗黙に推論される型で宣言します。 nUprExpression - LwrExpression としたとき、ScopeStatementn 回実行されます。 もし UprExpressionLwrExpression 以下であったならば、 ScopeStatement の実行回数はゼロになります。 Foreachforeach のとき、変数はまず LwrExpression に設定され、本体を1回実行するたびにその後に1ずつ増えていきます。 Foreachforeach_reverse の時は、変数はまず UprExpression に設定され、本体実行の前に1ずつ減っていきます。 ScopeStatement の実行回数に関係なく、 LwrExpressionUprExpression はどちらもちょうど1回だけ実行されます。

import std.stdio;

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

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

出力:

foo0123456789

foreach からの break と continue による脱出

foreach 内での BreakStatement はforeachのループを終了し、 ContinueStatement はすぐに制御を次の要素に移します。

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文へ移ります。

switch 文には必ず default 節が必要です。

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

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

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

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

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

ScopeStatementList は空であるか、または最後の case 以外、 ContinueStatement, BreakStatement, ReturnStatement, GotoStatement, ThrowStatement , assert(0) のいずれかで終える必要があります。 これは C言語の間違いやすい暗黙の fall-through から決別するための処置です。 fall-throught を明示するには goto case; を使います。

int number;
string message;
switch (number)
{
  default:    // 正しい: 'throw' で終わる
    throw new Exception("unknown number"); 

  case 3:     // 正しい: 'break' で終わる ('switch' を抜けます)
    message ~= "three ";
    break;

  case 4:     // 正しい: 'continue' で終わる (周囲のループを continue します)
    message ~= "four ";
    continue;

  case 5:     // 正しい: 'goto' で終わる (次の case への明示 fall-through)
    message ~= "five ";
    goto case;

  case 6:     // エラー: 暗黙の fall-through
    message ~= "six ";

  case 1:     // 正しい: case の中身が空
  case 2:     // 正しい: これはswitch文の最後のcase
    message = "one or two";
}

break 文は switch の BlockStatement を抜けます。

文字列も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 Identifieropt ;
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 Identifieropt ;
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 Expressionopt ;
返値を定めて、 現在の関数を終了します。 void以外の返値型を持つ関数ならば、 Expression が必要です。 Expression は暗黙の内に返値型へ変換されます。

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

関数が完全に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文を囲む最も内側の SwitchStatement DefaultStatement へ処理を移します。

三番目の形 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節は全て実行され、 同期オブジェクトは解放されます。

初期化をスキップするような GotoStatement は不正です。

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 の宣言を隠している
  }
}

synchronized 文

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 { ... }

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

synchronized 文は再帰ロックに対応しています。つまり、 synchronized でラップされた関数が自分自身を再帰呼び出しすることは可能で、 期待した通りの動作になります: mutex は再帰の回数分のロックとアンロックを行います。

try 文

例外処理は、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 は全ての例外をキャッチします。

The 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インスタンスが存在する場合も、 それらのデストラクタも、ソースコード上の逆順で ScopeGuardStatement の間に挟まって実行されます。

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

try {
  scope(exit) write("2");
  scope(success) write("3");
  scope Foo f = new Foo();
  scope(failure) write("4");
  throw new Exception("msg");
  scope(exit) write("5");
  scope(success) write("6");
  scope(failure) write("7");
}
catch (Exception e) {
}
writeln();
の時は:
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
{
  /* ... なんらかの代替案 ... */
}

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

pragma 文

PragmaStatement:
    Pragma NoScopeStatement

mixin 文

MixinStatement:
    mixin ( AssignExpression ) ;

AssignExpression にはコンパイル時に定数文字列に評価される式を指定します。 文字列の中身は文法的に妥当な 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
}