D言語の更新まとめ 2020年5月版（dmd 2.092.0）

# はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである `2.092.0` が 2020/05/10 にリリースされました。

最近はメモリ安全性への取り組みが活発で、利便性強化以外にも警告強化や「所有権と借用」のプロトタイプ実装が取り込まれるなど安全性への配慮が目玉となっています。

所有権に関してはまだまだ不具合があるとChangeLogにも書き残されていますが、実際にオンラインエディタでも試せるようになっているので、雰囲気は十分感じることができるのではないかと思います。（絶賛修正中なので `nightly` で試すのもおすすめです）

より細かい内容は下記リンクからChangeLogをご覧ください。

- ChangeLog
    - https://dlang.org/changelog/2.092.0.html

また、前回3月ののリリースまとめは以下になります。

- D言語の更新まとめ 2020年3月版（dmd 2.091.0）
    - https://qiita.com/lempiji/items/85d73a79368ea26ffb58

# 変更点目次

## コンパイラ変更点
- コマンドライン引数である `-revert=import` と `-transition=checkimports` が削除されました
- マングリングのため、C++のGNU ABIタグがサポートされました
- `extern(D)` がついていないモジュールコンストラクタとモジュールデストラクタが非推奨になりました
- DIP25が既定で有効になり、違反した場合は警告が発生するようになります
- ポインタに対する所有権と借用のプロトタイプが追加されました
- ストレージクラスの `in` を `scope const` とする `-preview=in` が追加されました
- `printf` と `scanf` のフォーマットと引数を検証するようになりました
- 環境変数の `SOURCE_DATE_EPOCH` をサポートするようになりました

### 改善点
* Bugzilla 20470: `AliasSeq` のタプルを介したアクセスで `this` が考慮されない点を改善
* Bugzilla 20609: `@disable` や `deprecated` の関数が候補に表示されてしまう点を改善
* Bugzilla 20625: 関数リテラルの呼び出しで引数不一致のときのメッセージが一部不適切な点を改善
* Bugzilla 20636: `asm` ブロックにて `RDSEED` 命令をサポート
* Bugzilla 20734: `scope` 引数に配列リテラルを指定したときはスタック確保になるよう改善

## ランタイム変更点
- C#やJavaにある `isAssignableFrom` のように使える `TypeInfo_Class/TypeInfo_Interface.isBaseOf` が追加されました
- `core.memory.pageSize` と `minimumPageSize` が追加されました

### 改善点
* Bugzilla 19924: `core.bitop.bswap(ulong)` がBetterCモードで動作するように改善
* Bugzilla 20711: `object.update` の `update` コールバックで更新値を返すためのコピーが不要な形式に対応
* Bugzilla 20741: 配列の `dup`, `idup` 、組み込みの連想配列の `keys`, `values` で型がPostblitであるかどうかを確認してコードを最適化するよう改善

## ライブラリ変更点
- `std.datetime.data` に `Date.isoWeekYear` と `Date.fromISOWeek` が追加されました
- `std.xml` が非推奨になりました
- `std.digest.digest` に定義された非推奨なエイリアスが削除されました

### 改善点
* Bugzilla 20723: x86/x86-64 でarc4randomでないとき、`std.random.unpredictableSeed` が `RDRAND` を使用するか試すようになります
* Bugzilla 20732: `swap` が `pure` `@nogc` `nothrow` を持たないコピーコンストラクタを持つ型をサポートするようになります


## DUB変更点
- 隠しディレクトリは無視されるようになりました
- `dub lint` が `--report-file` 引数をサポートするようになりました

# 注目トピック
## 言語・コンパイラ変更点
### 強化：環境変数の `SOURCE_DATE_EPOCH` をサポートするようになりました

環境変数の `SOURCE_DATE_EPOCH` にUNIXタイムスタンプの値を設定すると、それが `__DATE__` と `__TIME__` の値として使われるようになりました。

任意の時間を偽装してコンパイルができるということで便利に使えそうですが、時間を使ってランダムにコンパイルエラーを起こしている各位には残念なお知らせということになりそうです。（残念？）


### 試験的：ポインタに対する所有権と借用のプロトタイプが追加されました

今回のアップデートの目玉の1つです。
いわゆる「所有権と借用（Ownership and Borrowing、通称OB）」です。

昨年7月、D言語作者のWalter Bright氏がに「段階的に所有権と借用の仕組みを追加していく」との方針を明らかにしました。
ちょくちょく「実装している」というコメントや開発状況は公開されていたのですが、今回そのプロトタイプがベータ版として正式に利用できるようになりました。

当初の表明に関しては以下のあたり参照ください。

- https://blog.kotet.jp/2019/07/ownership-and-borrowing-in-d/
- https://qiita.com/lempiji/items/e114dcdfdeeed2e9064c

今はまだ制限や不具合も多いとのことですが、「特定条件を満たしたときのみ有効になる」というチェック機構なので、そこまで混乱を招くことはないと思われます。

コンセプトや細かい解説をするには余白が足りないので別の記事にするとして、ここでは使い方と最もシンプルな例のみ触れて雰囲気をつかんでおきたいと思います。

#### 使い方

所有権と借用のチェックを有効にするには、以下2つの指定が必要です。

- コンパイラ引数として `-preview=dip1021` を指定する
- チェック対象の関数に `@live` 属性を付与する

#### 動作例

というわけで、何の小細工もないシンプルにダメな「二重解放」の処理を試してみます。
オンラインエディタに貼り付けて実行できますので、興味があればお好きにifやforなど組み合わせてみてください。

```d:app.d
import core.stdc.stdlib;

void main() @live // @live属性を付与
{
    auto p = cast(byte*) malloc(1); // 1バイト確保
    free(p);
    free(p); // 2度目のfreeは不正な処理
}
```

```console:コンパイル
dmd main.d -preview=dip1021
```
※ 2020/05/06現在、オンラインエディタでは `-preview` 引数の指定は不要となっています（理由不明）

```console:コンパイル結果
onlineapp.d(7): Error: variable onlineapp.main.p has undefined state and cannot be read
onlineapp.d(7): Error: variable onlineapp.main.p is not Owner, cannot consume its value
```

##### エラーの読み方

処理としては `malloc` で確保したポインタを2回 `free` 関数に渡し誤って開放してしまう、という典型的な Double-Free という問題です。2番目の `free` の呼び出しが不適切です。

これに対しエラーは2つ発生しており、それぞれ「未定義状態のポインタを読み取る」「所有権を持たないポインタの移譲」について怒られています。
ちなみに `(7)` というのが行数で、2番目の `free` 関数の呼び出し箇所ですのでバッチリですね。

どちらのエラーも、原因としては「ポインタ `onlineapp.main.p` が、所有権を要求する `free` 関数に一度渡されているので、その後のポインタの状態が `undefined` という未定義状態になっている」ことによって発生します。
（`onlineapp.main.p` というのは、オンラインエディタで実行したので既定の `onlineapp` モジュールの `main` 関数の変数 `p` を表しています）

__1つ目のエラー__は、1度解放したポインタをもう1度 `free` 関数に渡そうとするわけですが、その時「ポインタを利用しようとする」ため、「未定義状態のポインタを読み取る」ということになってエラーが発生しています。

__2つ目のエラー__は、「`free` 関数は所有権を要求する」ので、「所有権を持たない（`undefined`状態の）ポインタは不適切だ」ということになってエラーが発生しています。

つまり関数の中で「ポインタの状態」というものをトラッキングしていて、使っても安全なのかどうかを随時確かめています。解析はちょっと重いらしいので、こういう関数は細かくするように心がけたいですね。

言語としての利便性を保ちつつ、こういった安全性への取り組みが追加されていくのは良いことだと思います。


### 変更： DIP25が既定で有効になりました

かなり地味ですが、こちらも今回のアップデートにおける目玉の1つと言える内容です。

DIP25とはなんなのかというと、「関数の `ref` 戻り値が破棄された値を指している可能性がある」という仕様の問題を解決するために作られた「`return ref` セマンティクス」というものを導入する提案です。

2015年、バージョン `2.067` の頃から実装されていてフラグを立てれば利用できたのですが、今回からこのルールが既定で有効になり、違反すると警告が出るようになりました。
これもSafe by default、既定で言語の安全性を高める運動の一環です。

言葉だけでは機能がわかりづらいので、ざっと例を見ていきます。

#### 問題が起きる例
まず何も処理をしない、何の問題もない `ref` 引数と `ref` 戻り値を使った関数があるとします。

```d:普段は問題のない関数
ref int identity(ref int n) {
    return n;
}
```

これを以下のように使うと、`fun` 関数の戻り値は適切でしょうか？というのが当初の問題です。

```d:問題のある使い方
ref int fun(int n) {
    return identity(n); // 引数の参照が返ってくる？
}

void main() {
    fun(10) += 10; // 参照先はどこ？可算しても大丈夫？
}
```

`fun` 関数の引数の有効期間は、当然ですが `fun` 関数の処理の中だけです。
では、この `fun` 関数の戻り値は正常な参照でしょうか？

`identity` 関数がいわゆるグローバル変数を返しているのであれば何の問題もありません。しかしライブラリがこうなっていたとして、使う側としては判別可能でしょうか？謎は深まるばかり。

そこでDIP25は、この `identity` 関数の中の「引数の参照を `return` する」という状況に対して以下のような警告を発生させます。

```console:
onlineapp.d(9): Deprecation: returning n escapes a reference to parameter n, perhaps annotate with return
```

引数の参照を戻り値に使っているので `return` で修飾した方が良いぞ、という内容です。

そこで以下のように引数にreturnを付けると、出ていた警告が消えます。

```d:return修飾した適切な関数
ref int identity(return ref int n) {
    return n;
}
```

これで、「戻り値と引数は同じ生存期間だ」とコンパイラに教えることができます。

すると今度は問題のある使い方をしていた `fun` 関数の `return` する箇所で同じ警告が出るわけです。

`identity` 関数の振舞いに関して正しい情報が得られたので、これで正しいバグ位置が特定できるようになりました。めでたしめでたし。

なお **C++で同じことをするとどうなるのか**、興味のある方は試してみると面白いと思います。

#### 自動推論

とまぁ毎回ここまで考えるのは大変だ、という意見もごもっともで、いくつか楽をする手があります。

まず将来強化する/ロジックが変わる、といった可能性に対し、どうしても今決められない場合は「とりあえず `return` を付けておく」というのが手です。
利用する側の条件は厳しくなりますが、 「`return` 修飾しても実際返さない」というのは特にエラーにはならないためです。

また、テンプレート関数やラムダ式に `ref` 引数がある場合は、`return` を付けるべきか自動的に推論されるようになっています。

というわけで、特定できない場合はテンプレート化してしまうのも手です。

```d:テンプレート化
ref int identity()(/* return */ ref int n) { // 自動推論されるため書かなくて良い
    return n;
}
```

これはUFCSを前提としたメソッドチェイン目的で作った関数でありがちな状況なのですが、自作されている範囲でも実は既に推論されているものがあるかもしれません。

## 強化： C++のGNU ABIタグがサポートされました

C++連携強化の一環で、「C++ライブラリを完全にサポートするための強化」とのことです。

本家の仕様はこちら。

- https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html

私も詳しくない上に「ほとんどの人は関係しない」とのことで、サンプル抜粋にとどめます。
分かる人は分かる系の内容だと思われるので、興味のある方は触ってみてください。

```d:
extern(C++):
@gnuAbiTag("tagOnStruct")
struct MyStruct {}
@gnuAbiTag("Multiple", "Tags", "On", "Function")
MyStruct func();
```

## 強化： `printf` と `scanf` の引数を検証するようになる

コンパイラが `core.stdc.stdio` の `printf` と `scanf` の引数を検証するようになります。
「書式文字列と引数があっているか検証する」ということで、チェック内容はC99の規格に準じているとのことです。

これらの関数は入出力を扱ううえで多くの不具合が発生する場所とされているので、これも安全向上の一環です。

もちろん普通にDの関数を使えば型チェック等は十分安全なのですが、BetterCモードとかもあるのでこういった機能は望まれているようです。

例はこんな感じです。

```d:
import core.stdc.stdio;

void main()
{
    int n = 100;
    printf("%s", n);
}
```

```console:結果
onlineapp.d(7): Deprecation: argument n for format specification "%s" must be char*, not int
```

警告のみなので、これ自体が障害になってコンパイルできなくなることはありません。
見たら気を付けたいですね。

ちなみになんでこんな特化型の処理ができるかというと、裏では `pragma(printf)` と `pragma(scanf)` という pragma が追加されています。
これらを付与することで、「`printf` のような引数を持つ関数」、「`scanf` のような引数を持つ関数」に対して同様のチェックを行うことができます。

フォーマット用の書式化文字列が動的に決まる場合は対象外なので、できるだけシンプルに注意して使いたいですね。

## 改善： タプルを介したアクセスで一部 `this` が利用可能になる

これも地味なのですが、随分~~魔術的~~便利そうな改善ポイントだったので取り上げておきます。

できるようになったのは、以下のように `AliasSeq` を使って**フィールドを使ったエイリアスが定義できる**という強化です。

```d:
import std.stdio;
import std.meta;

struct S
{
    int a, b;
    alias AliasSeq!(b, a) expandReverse;
}

void main()
{
    S obj;
    obj.expandReverse = AliasSeq!(1, 2);
    writeln(obj); // S(2, 1)
}
```

今までは `this` が考慮されずに展開されたため「`this` の指定が足りない」という感じのエラーになっていました。

パッと思いつく限り、Vector型のSwizzle演算などに活用の道がありそうですが、利便性向上とメタプログラミングが捗りそうな強化で大変面白そうです。
（実際Swizzle演算をやるなら `opDispatch` のほうが正攻法かもしれませんが）

## ランタイム変更点

### 強化： `TypeInfo_Class/TypeInfo_Interface.isBaseOf` が追加されました

`isBaseOf` というあるクラスが特定のクラスやインターフェースを継承/実装しているかどうか、を実行時型情報からチェックする関数が追加されました。

使い方としては以下の通りです。

```d:
class ClassA {}
class ClassB : ClassA {}

auto a = new ClassA(), b = new ClassB();

assert(typeid(a).isBaseOf(typeid(a)));
assert(typeid(a).isBaseOf(typeid(b)));
assert(!typeid(b).isBaseOf(typeid(a)));
```

またC#やJavaでは `isAssignableFrom` という名前だそうですが、Dでは `opAssign` によって代入演算のオーバーロードができるので、あえて継承のみに絞った名前としているそうです。


### 強化： `core.memory` に `pageSize` と `minimumPageSize` が追加

それぞれシステムがメモリを管理する際に使うページサイズを取得できるようになります。

違いとしては、

- `minimumPageSize` はコンパイル時に利用できるプラットフォーム固有の値
- `pageSize` は実行時に設定される実行環境の値

です。

ということで通常は `pageSize` を使うことが推奨されています。

こういった値がランタイムに組み込まれることで、より手軽にポータブルでパフォーマンスの高いメモリ管理の仕組みが作れるようになりそうですね。

## ライブラリ変更点

### 強化： `Date.isoWeekYear` と `Date.fromISOWeek` が追加されました

いずれも、日付等に関する国際規格である `ISO 8601` で定義された `ISO week date system` という仕組みに基づくメソッドです。
主に会計用途とのことで、あまり縁のない方のほうが多いかもしれません。

- ISO week date
    * https://en.wikipedia.org/wiki/ISO_week_date


`Date.fromISOWeek` は `Date` 型の staticメソッドで、 要は「何年」の「何周目」の「何曜日」という情報から実際の日付を計算して `Date` 型のオブジェクトを取得します。

`isWeekYear` は `Date` オブジェクトのプロパティで、「ISO week dateにおける年」を取得するプロパティです。

ただし第1週目の定義として、「その年の第1木曜日を含む週が1週目（引数的には0）」ということで、年末年始が通常の西暦（グレゴリオ暦）とは異なる年にカウントされることが多々あります。

たとえば、2020年の第1木曜は `1月2日` なのですが、同週の月曜を見ると `2019年12月30日` です。
結果、 `Date.fromISOWeek(2020, 1, DayOfWeek.mon)` は `2019年12月30日` ということになります。

ということであまり通常使いするようなものではないかもしれません。
おそらく便利に使える職種の方が要望されたものかと思います。


### 変更： `unpredictableSeed` が `RDRAND` を使用するようになる

`unpredictableSeed` というのは乱数シード用の予測不能な値を返すもので、`RDRAND` というのはハードウェア乱数命令です。
よりセキュアな乱数シードが使われるということで、そういった機能を求める方含め多方面に活用されやすくなる変更かと思います。

ちなみにハードウェア乱数が使われているかどうかは、 `core.cpuid` モジュールの `hasRdrand` 関数によって判定されています。自分の環境が気になる人はこのフラグを確認してみてください。

また言語機能のほうでも `asm` ブロック内で `RDSEED` 命令を使えるようになる、という改善が行われています。
これもハードウェア乱数の一種が使える命令ですが、 `RDRAND` よりも予測可能性の面で有利とのことです。ただし幾分新しい命令でCPUを選ぶので、標準ライブラリとしては現状 `RDRAND` を使っておくのが妥当そうです。

- The Difference Between RDRAND and RDSEED
    * https://software.intel.com/en-us/blogs/2012/11/17/the-difference-between-rdrand-and-rdseed

### 改善： `object.update` のコールバックが `ref` 形式に対応

`object.update` というのは、連想配列の値を登録または更新するためのメソッドです。

`k1` というキーがなければ `1` を追加、キーがあれば値に `1` を足すという処理をするとき、元々以下のように利用するものです。

```d:
int[string] aa;
aa.update("k1", { return 1; }, (int n) { return n + 1; });
```

この2番目の引数が `ref` に対応します。

```d:
aa.update("k1", { return 1; }, (ref int n) { n += 1; });
```

強化の動機としては戻り値をコピーするコストが高いということで、「`ref` にすれば戻り値は不要だろう」とのことからこうなったようです。


## 非推奨または廃止される機能

### 廃止された機能

今回の更新では、非推奨になったものが2つ（実際は3つ）、削除されたものが2つあります。

__非推奨__

- `extern(D)` がついていないモジュールコンストラクタとモジュールデストラクタが非推奨になりました
- `std.xml` が非推奨になりました

__削除__

- コマンドライン引数である `-revert=import` と `-transition=checkimports` が削除されました
- `std.digest.digest` に定義された非推奨なエイリアスが削除されました

#### `extern(D)` ではないモジュールコンストラクタ/デストラクタが非推奨

これは `static this() {}` や `shared static ~this() {}` といったモジュールコンストラクタ/デストラクタを `extern(C)` などと組み合わせて使うのは非推奨とする、というものです。

元々 `extern(C)` というのはマングリング規則を変えるような作用を持ちますので、モジュールコンストラクタの呼び出しの際にマングリングが衝突する可能性がある（ただし可能性は非常に低い）、という理由です。

どうしても `extern(C)` してるブロックで定義したいなと思ったら、個別に `extern(D) static this() {}` とすれば良いので、警告を見たら個々に追加してやれば良いかと思われます。


#### `std.xml`が非推奨

標準ライブラリからXML操作に関するモジュールが非推奨となりました。
理由としては、設計的に古く最新の言語機能やデザインにそぐわないこと、代替ライブラリがそれなりに出てくるようになったこと、の大きく2点のようです。

公式のChangeLogには `dxml` というパッケージがおすすめされています。

2020年5月現在、候補となるライブラリは `dxml` を含め3つほどありますのでざっと紹介しておきます。

- `dxml`
    * https://code.dlang.org/packages/dxml
- `rapidxml`
    * https://code.dlang.org/packages/rapidxml
- `arsd-official:dom`
    * https://code.dlang.org/packages/arsd-official%3Adom
    * 主にHTML用なので厳密にはXML用途ではありませんが、目的によっては利用可能なので取り上げています

ざっくり機能を比較すると以下の通りです。

| | `dxml` | `rapidxml` | `arsd-official:dom` |
|:--:|:--:|:--:|:--:|
| 特徴 | XML1.0の仕様テストをクリア。Range対応の解析器を持つ | C++のrapidxmlを再実装したもの | 大規模サブパッケージ集の1つ。HTMLやRSSの解析でも利用されている。 |
| 名前空間の対応 | 〇 | 〇 | △ |
| XPath | △ | ☓ | ☓ |
| 速度 | ？ | 〇 | ？ |
| おすすめ度 | ◎ | 〇 | 〇 |

3つ挙げてみましたが、この中ではやはり仕様テストまで設けている `dxml` が安心して利用できるかな、というところです。

ただ、 `rapidxml` はその名の通り「高速」が特徴で、C++の本家公式が「可能な限り最速を目指す」と公言するほどパフォーマンス重視のライブラリです。
再実装したのはD言語のWebフレームワークを作っている `huntlabs` というグループなのですが、元々持っていた `hunt-xml` より後にこのライブラリを公開しているので、かなり速度的なメリットがあってのことだと思われます。

調べて意外だったのは、かなり大規模なパッケージ集である `arsd-official` の中に `dom` というサブモジュールがあり、これがRSSの解析に使われていたことです。さすが何でもあり。
目立った機能不足は名前空間などの対応があるようですが、基本的に外部データに対して使って実績があるのは強みであり、特化型として結構使える可能性は高いと思われます。

実は他にも個人製作ながら `pham_xml` という、機能的にはXPath含め最も豊富で、標準ライブラリと比較してもメモリ消費と速度ともに3倍以上の効率、とのことから期待大のパッケージがあります。
しかしモジュール構成などがdubパッケージの標準形式にあっておらず、現時点では使おうとするとビルドエラーが出るので、組み込むのであればForkして使うなどの工夫が必要そうです。


#### `-revert=import` と `-transition=checkimports` が削除

ざっくり言ってしまうと、今はもう特に何もしていないフラグなので消した、というものです。
細かい情報は見つけられなかったのですが、特に影響は無さそうです。

元々 `-revert=XXX` や `-transition=XXX` というのはコンパイラフラグの実装パターンの1つで、「既に実装された機能だがどうしてもオフにしたい」「移行段階にある機能の動作を切り替えたい」というときに使うものです。

今はもう広く浸透したものであれば、メンテナンス等考えると順次削っていくのが健全かと思います。

バージョンアップで困ったときにこのあたりのフラグを思い出すと何かしら役に立つかもしれませんね。


#### `std.digest.digest` の非推奨エイリアスが削除

消えたエイリアスは以下の通りです。
たくさんありますが、いずれも `std.digest` に同名の定義がありますので、そちらをimportすればコード変更は不要なはずです。

ちなみに `std.digest.digest` 自体も非推奨となり、 `2.101` で削除予定となっています。

- isDigest
- DigestType
- hasPeek
- hasBlockSize
- digest
- hexDigest
- makeDigest
- Digest
- Order
- toHexString
- asArray
- digestLength
- WrapperDigest
- secureEqual
- LetterCase

## DUB変更点

### 強化： `dub lint` が `--report-file` 引数をサポート

`dub lint` はコードのフォーマット等をチェックして不適切な箇所について警告等を発生させるコマンドです。

これに対し `dub lint --report-file report.json` と実行すると `report.json` ファイルに結果を出力できるようになります。

結果がJSON形式なのがポイントでしょうか。分析したりDIFF見るのに良いかもしれませんね。


# まとめ

公式に掲げられた安全性強化の目標を中心に、いくつかの安全強化策の追加、加えてC++連携や利便性の強化が着々と行われるリリースになったと思います。

また、ついに所有権と借用の仕組みが入るときが近づいてきてきました。

とはいえ標準ライブラリや各種DUBパッケージが完全に対応するまでは様子見の人が大半かと思いますので、これからの強化に期待しつつ、まずは言語的に興味のある方にチャレンジしてみてもらえればと思います。

というわけで次回、プロトタイプから正式になるのか、はたまた別のトピックがあるのか、7月版を期待して待ちたいと思います！