はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.094.0
が 2020/09/22(ChangeLog日付より) にリリースされました。
今回はコンパイラの強化を中心にいくつか新機能が追加されています。
変更自体は結構数があるのですが、目立つもの中心にご紹介していきたいと思います。
より細かい内容は下記リンクからChangeLogをご覧ください。
- ChangeLog
また、前回7月ののリリースまとめは以下になります。
- D言語の更新まとめ 2020年7月版(dmd 2.093.0)
変更点目次
コンパイラ変更点
-
__traits(child, parent, member)
が追加されました -
import
と合わせて使うallMembers
のtrait
動作が修正されました- 追記 : Beta版を基に記載していましたが、コメントの通り影響が大きくRevertされました
- 配列リテラルの型判定が改善されました
-
-vtemplates
フラグによるテンプレートの利用状況診断が改善されました- 2020/09/27追記(ベータになく正式リリースで追加)
- C++ヘッダー生成でコメント生成が既定で抑制されるようになります
- 例外をスローするコードが
debug
ブロックで使えるようになりました -
-inline
コンパイラスイッチに関わらずpragma(inline, true)
の関数が常にインライン化されるようになりました - 暗黙のオーバーライドに関する非推奨期間が終了しました
- ベクトル型について厳格な変換ルールが定められました
-
in
でマークされた引数が適切に表示されるようになりました - DDocにおけるマークダウンサポートが標準で有効になりました
- 入力パラメーターのサポートが追加されました (
in
引数および-preview=in
) - サポートされていない
-mcpu=
スイッチでベクトル型や操作を行うとエラーになるようになります
ランタイム変更点
- 構造体の配列における等価チェックが
v2.078
以前と同じになります -
core.memory.GC.allocatedInCurrentThread()
が追加され、-profile=gc
が高速になりました
インストーラー変更点
- インストールスクリプトで
get-path <compiler>
が追加されました - DMDのバイナリがLDCでビルドされたものになります
DUB変更点
-
windows-dmd
のためにx86_omf
アーキテクチャが追加されました -
dub remove --non-interactive
は既定ですべてのバージョンを削除するようになります -
dub upgrade
をパッケージではないフォルダで実行しても何も処理しなくなります - コミットを指定する
git url
による依存関係サポートが追加されました - すべてのコマンドが
package[@<version-spec>]
形式をサポートし、--version
が非推奨になります
注目トピック
言語・コンパイラ変更点
強化: __traits(child, parent, member)
が追加されました
__traits(child, parent, member)
という形式の trait
が追加されました。
用途としては __traits(getMember, parent, member)
とほぼ同じで、何らかのオブジェクトに対してメンバーアクセスを行う機能です。
コードの雰囲気としては、C++のメンバー変数へのポインタが近いかもしれません。(写経以外で使ったことありませんが…)
child
が getMember
と異なるのは、第3引数に alias
等で定義した「パス情報」を渡す点です。(getMember
は名前文字列)
ChangeLog抜粋ですが、構造体 A
に対し i
や foo
へのアクセスを以下のように書くことができます。
struct A
{
int i;
int foo(int j) {
return i * j;
}
}
alias Ai = A.i;
void main()
{
A a;
__traits(child, a, Ai) = 3;
assert(a.i == 3);
assert(__traits(child, a, A.foo)(2) == 6);
}
強化: -vtemplates
フラグによるテンプレートの利用状況診断が改善されました
2020/09/27追記
正式リリースの際にChangeLogに追記されていました。
前回 2.093.0
で追加された -vtemplates
スイッチが強化されました。
今回はテンプレートのインスタンス化情報を俯瞰的に見るにあたって、「何のテンプレートがどこで何を引数に行われたのか」という情報を表示する -vtemplates=list-instances
スイッチが追加されています。
たとえば以下のコードで
void main()
{
enum count1 = Count!(int, short, byte);
enum count2 = Count!(int, short, byte);
enum count3 = Count!(string, int, short, float, bool, Object);
}
template Count(Args...)
{
enum Count = Args.length;
}
以下のような出力が得られます。
source\app.d(8,1): vtemplate: 3 (2 unique) instantiation(s) of template `Count(Args...)` found, they are:
source\app.d(3,19): vtemplate: explicit instance `Count!(int, short, byte)`
source\app.d(4,19): vtemplate: explicit instance `Count!(int, short, byte)`
source\app.d(5,19): vtemplate: explicit instance `Count!(string, int, short, float, bool, Object)`
強化のポイントとしては「インスタンス化された位置」「インスタンス化したときの引数」が分かることでしょうか
以前は「回数」と「何種類」だったので、今回の方が改善ポイントが分かりやすいと思われます。
なお std.meta
などのモジュールを使い始めると一気に出力が増えるので注意が必要です。
ターミナルやコマンドプロンプトで直接見るというよりは、一度テキストに出力してじっくり見るのが良いかと思います。
変更: C++ヘッダー生成でコメント生成が既定で抑制されるようになります
Dではコンパイルの際、-HC
スイッチを指定するとC/C++向けのヘッダーファイルが自動生成できます。
引数は以下のように設定することができ、それぞれ以下のような意味があります。
-
-HC=silent
-
extern(C)
またはextern(C++)
のみ生成対象とします
-
-
-HC=verbose
- 生成時に無視される宣言もコメントとして出力されます(主に
extern(D)
、通常のD向け関数)
- 生成時に無視される宣言もコメントとして出力されます(主に
普通は -HC
だけを指定して使うものなのですが、今までは -HC=verbose
が既定値でした。
しかし場合によってはコメントが大量に生成されると邪魔だということで、今回のリリースから silent
のほうが既定値となる、という変更です。
改善: ベクトル型について厳格な変換ルールが定められました
ベクトル型自体あまりなじみがないかもしれませんが、以下ドキュメントにある __vector
や float4
などのSIMD演算をサポートする型のことです。
core.simd
に様々な型が定義されており、今までこれらの型はかなり柔軟な暗黙変換が可能になっていました。
しかし今回から安全面の強化から変換ルールが厳格になります。
といってもサイズや値型に関して順当なチェックが追加されるだけで、使いこなすような方なら問題になるコードは書かれてないかと思います。
コード書きながら類推が可能なレベルではあるので、ちょっと書き間違ったときなどに有効かもしれません。
const int4 a = 5;
int4 b = a; // 両方 int4 のためOK
void16 c = a; // 同じサイズの void にキャストするのはOK
float4 d = b; // int4 から float4 への暗黙変換は不可のためエラー
なお明示的なキャストを使えば同じサイズの型には代入が可能です。
long2 a = 8;
short8 b = cast(short8)a; // OK
short16 c = cast(short16)b; // サイズが異なる型にはキャストできないためエラー
改善: サポートされていない -mcpu=
スイッチでベクトル型や操作を行うとエラーになるようになります
-mcpu
というコンパイラスイッチは、CPUが持っている AVX
系ベクトル演算命令のサポート状況を指示するために使います。
要は「効率的な命令セットが使えることを教えれば高速化できる」というもので、以下の指定ができます。
-
-mcpu=baseline
- 既定値、ターゲット環境の基本的な命令だけ利用できることを指示します
-
-mcpu=avx
- 浮動小数点数のベクトル型に対して、
SSE
命令の代わりにAVX
を使えることを指示します
- 浮動小数点数のベクトル型に対して、
-
-mcpu=avx2
-
AVX2
系の命令が使えることを指示します
-
-
-mcpu=native
- 実行環境のサポート状況に合わせて切り替えます
参考:https://dlang.org/dmd-windows.html#switch-mcpu
今回のリリースから __vector
型がターゲットCPUの設定を見て型と演算子の組み合わせから適切にエラーを発生させるようになります。
例えば以下のようなコンパイル条件が書けるようになったりします。
static if (__traits(compiles, long4))
{
// `-mcpu=avx` 以上の指定でコンパイルされます
}
static if (__traits(compiles, { int4 x; x += 1; }))
{
// `-mcpu=baseline` 以上の指定でコンパイルされます
}
static if (__traits(compiles, { int4 x; x *= 1; }))
{
// `-mcpu=avx` 以上の指定でコンパイルできます
}
static if (__traits(compiles, { int8 x; x += 1; }))
{
// `-mcpu=avx2` 以上の指定でコンパイルできます
}
どの型でどの演算子がサポートされるのか、表にしたものがありますので気になる方はChangeLogのほうご覧ください。
試験的: 入力パラメーターのサポートが追加されました (in 引数および -preview=in)
今回の目玉機能の1つです。
D言語の引数は、元々 in
, ref
, out
という3種類のストレージクラスで修飾することができます。
このうち ref
と out
は比較的使われているものの、 in
というのは「入力として使う」という意味から scope const
のエイリアスとして定義されていました。その後 DIP1000 という提案を受けて単に const
の意味になるなど紆余曲折あり、その機能性の低さからあまり使われませんでした。
今回のリリースではこの試験的に追加されていた -preview=in
フラグの意味共々見直し、より機能的な宣言として意味論が見直されることになりました。
ざっくりいうと、コンパイラに -preview=in
フラグを渡し関数の引数を in
と修飾することで、引数のコピーコストが下がるような実装が自動的に選択されるようになります。
これにより様々なオーバーロードを用意する必要がなくなるなど良いことがいくつかあります。
この機能で覚えておくべきことは以下の3点+αです。
- コピーコストの高いオブジェクトは自動的に参照渡しになる
- コピー動作またはデストラクタがある場合は参照渡し
- コピー動作とは Postblit(いわゆる
this(this) {}
) かコピーコンストラクタ
- コピー動作とは Postblit(いわゆる
- コピー禁止の場合は参照渡し
-
@disable this(this);
としている構造体など
-
- 元が参照型の場合は値渡し
- 動的配列、連想配列、関数ポインタ、
delegate
、クラス
- 動的配列、連想配列、関数ポインタ、
- それ以外は、マシンワードの2倍を超えると参照渡し(ただしバックエンド実装で変えて良い)
- コピー動作またはデストラクタがある場合は参照渡し
- 右辺値も参照として受け入れられるようになる
- 参照渡しかどうかは自動的に判定されるため、
in ref
と書いているとエラーとなる
といっても分かりづらいので、フラグを変えたときに挙動が変わるサンプルを見てみたいと思います。
構造体Sのデストラクタで文字列を出力していますが、 -preview=in
の有無によって順序が変わります。
(もっと短くできそうですが、理解不足のためこれが精いっぱいでした…)
import std;
void main() {
test(S(10) + S(20));
}
struct S {
int n;
~this() {
writeln("dtor : ", n);
}
// 引数をinで修飾
S opBinary(string op : "+")(in S rhs) const {
return S(n + rhs.n);
}
}
void test(S obj) {
writeln(obj.n);
}
dtor : 20
30
dtor : 30
dtor : 10
30
dtor : 30
dtor : 20
dtor : 10
実行結果を見た感じ、 -preview=in
のほうは構築順の逆順なので比較的分かりやすそうです。
これはデストラクタの有無で動作が変わっているパターンで、opBinary
の引数にあたる 20
で初期化した構造体が実際は参照渡しになっています。
結果、デストラクタの評価タイミングが関数呼び出しの前か後か変わる、ということになっています。
とまぁすべてを理解して書くのは大変なので、「入力として使うため変更しない」「サイズが大きいかもしれない」というときに「引数に in
とつける」というくらいで十分かと思います。
元々 scope const
の意味だったこと踏まえ、同じような修飾が出てきた場合に付けると良いかもしれません。
ランタイム変更点
強化: core.memory.GC.allocatedInCurrentThread()
が追加され、-profile=gc
が高速になりました
allocatedInCurrentThread
というのは、現在のスレッドで割り当てられたメモリ量を得る関数です。
元々 core.memory.GC.stats().allocatedInCurrentThread
として取得できる値と同じですが、これは他にもいろいろな統計情報を取るためやや重い処理となっていました。
今回追加された関数は割り当てメモリサイズだけを取得するため幾分高速になっています。
こちらメモリ割り当てのプロファイリングをする -profile=gc
スイッチの内部動作でも使われるため、プロファイルも同様に高速化されているとのことです。
インストーラー変更点
強化: インストールスクリプトで get-path <compiler>
が追加されました
インストールスクリプトで使える get-path
アクションが追加されました。
文字通りコンパイラ等のパスを得るコマンドで、以下のような引数と共に使います。
-
--install
- ローカルにコンパイラが見つからない場合インストールします
-
--dmd
- dmd相当のインターフェースを持った実行ファイル(
ldmd2
など)のパスを返します
- dmd相当のインターフェースを持った実行ファイル(
-
--dub
-
dub
の実行ファイルのパスを返します
-
公式サンプル抜粋すると、使い方は以下のようなイメージです。
curl https://dlang.org/install.sh | bash -s get-path --install
/home/user/dlang/dmd-2.093.0/linux/bin64/dmd
~/dlang/install.sh get-path ldc-1.23.0 --install
/home/user/dlang/ldc-1.23.0/bin/ldc2
~/dlang/install.sh get-path --dmd ldc --install
/home/user/dlang/ldc-1.23.0/bin/ldmd2
~/dlang/install.sh get-path --dub ldc --install
/home/user/dlang/ldc-1.23.0/bin/dub
~/dlang/install.sh get-path --dub dub-1.22.0 --install
/home/user/dlang/dub-1.22.0/dub
~/dlang/install.sh get-path --dub ldc,dub-1.22.0 --install
/home/user/dlang/dub-1.22.0/dub
改善: DMDのバイナリがLDCでビルドされたものになります
前にも同じような話があったなと思って振り返ると、2.091.0
(2020年3月)のリリースでWindows用インストーラーにバンドルされるDMDがLDCビルドになっていました。
今回はこれにならい、Linux向け、OSX向け、FreeBSD向けのバイナリがLDCでビルドされるようになり、コンパイル時間が20~30%の高速化されるだろうとのことです。
またこのリリースに際して以下の制限があるとのことで、該当する方はご注意ください。
- FreeBSD向け32bitコンパイラが提供されなくなります(FreeBSD向けの32bit LDCコンパイラが提供されないため)
- 以前のDMDリリースに含まれていたバイナリツールが含まれなくなります(optlinkは含まれます)
- 必要な場合は https://digitalmars.com/ で提供されているためこちらを参照してください
DUB変更点
強化: コミットを指定する git url
による依存関係サポートが追加されました
これも今回の目玉の1つです。
dub
の依存関係に git
のURLが直接指定できるようになりました。
企業内利用では公式のdubレジストリ以外で管理されるパッケージの比重が大きいとのことで、今回かなり直接的なサポートが追加された格好になります。
※元々 dubproxy など使って独自パッケージを透過的に扱っていました
今後、dub.json
であれば以下のようにURLとコミットハッシュを指定することで直接利用できるようになります。
{
"name": "git-dependency",
"dependencies": {
"gitcompatibledubpackage": {
"repository": "git+https://github.com/dlang-community/gitcompatibledubpackage.git",
"version": "ccb31bf6a655437176ec02e04c2305a8c7c90d67"
}
}
}
強化: すべてのコマンドが package[@<version-spec>]
形式をサポートし、 --version
が非推奨になります
見出しの通りで、npm
など多くのCLIツールに見られるバージョン指定方法がサポートされました。
指定が無ければ最新版なので、メジャーバージョンアップに追従できてない場合などが主なターゲットになります。
試しに使うと以下のようになります。
dub add stdx-allocator@3.0.2
非推奨または廃止される機能
今回の更新では、非推奨と廃止が1件ずつありますが、どちらもあまり使われるものではないため影響は小さいかと思われます。
廃止
- 暗黙のオーバーライドに関する非推奨期間が終了しました
非推奨
-
dub
の--version
が非推奨になります
廃止: 暗黙のオーバーライドに関する非推奨期間が終了しました
D言語のクラスでは、すべてのメソッドが既定で仮想関数でありオーバーライドが可能です。
継承して override
修飾がない場合、多くの場合はエラーになりますが、以下のように警告だけのケースがありました。
class Base
{
void myOtherFunction(void* ptr);
}
class Child : Base
{
// 暗黙的に `Base.myOtherFunction(void*)` をオーバーライドするためエラー
void myOtherFunction(const void* ptr);
}
今回から以下のようなエラーになるため、見つけたら override
を付けてやればOKです。
onlineapp.d(18): Deprecation: cannot implicitly override base class method onlineapp.Base.myOtherFunction with onlineapp.Child.myOtherFunction; add override attribute
なおこちら 2017/07/19 リリースの 2.075.0 から警告が出るようになっていました。
多くの場合すでに対応されていると思いますので、そこまで混乱を招くものではないかと思います。
非推奨: dub
の --version
が非推奨になります
元々 dub
のパッケージバージョンは --version
を指定する必要がありました。
dub add stdx-allocator --version=3.0.2
今回から以下の記述が統一記法として採用されていますので、こちらを利用するようにすればOKです。
dub add stdx-allocator@3.0.2
まとめ
今回は dub
コマンド周りの強化と in
パラメータの試験的機能追加に加え、ベクトル周りでより安全性が増すなど幅広い改善があったリリースでした。
今回はC++連携周りでいろいろあったようで、リリース予定から半月ほど遅れている状況でした。しかし不具合修正など十分なボリュームをもってリリースされたので結果良しだったように思います。
また、リリースの裏では名前付き引数の提案が受け入れられるなど、かなり大規模な強化への道が固まりつつあります。
次回は11月1日がリリース予定です。
引き続きの強化を期待して待ちたいと思います!