はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.105.0
が 2022/08/02 にリリースされました。
今回は前回からほぼ2ヶ月でのリリースで、内容はValgrindというメモリチェックツール連携の目玉を中心に、利便性の改善が多い印象です。
今回も一通りの内容ご紹介していきます。
より細かい内容は下記リンクからChangeLogをご覧ください。
- ChangeLog
また、前回である6月版のリリースまとめは以下になります。
- D言語の更新まとめ 2023年6月版(dmd 2.104.0)
変更点目次
コンパイラ変更点
-
alias this
に代入スタイルの構文が許可されるようになりました。 -
catch
節は、const
またはmutable
な例外のみを取る必要があります。 - 関数は
enum
ストレージクラスの指定ができなくなりました。 -
extern(C)
関数をオーバーロードすることはエラーになりました。 -
public
メソッドでオーバーロードされたprivate
メソッドへのアクセスは非推奨フェーズが終了しました。 - 事前定義されたバージョン識別子
VisionOS
が追加されました。
改善点
- Bugzilla 4663:
static
の位置に関する誤ったエラーメッセージ - Bugzilla 15436: コンパイラがまだ
AliasSeq
を"tuple"として参照している(TypeTuple
も同様か?) - Bugzilla 23475: Windows上で
ulong
/long
を使用したprintf
の非推奨メッセージが分かりにくい - Bugzilla 23871: ImportC:
__attribute
が認識されない - Bugzilla 23877: ImportC:
byteswap.h
のインポートにより、core.bitop.byteswap
への未定義の参照が生じる - Bugzilla 23886: ImportC プリプロセッサディレクティブ `#ident`` はサポートされていません
- Bugzilla 23928: エラーメッセージの改善: スコープ変数sは、abcを呼び出す非スコープパラメータに割り当てられています
- Bugzilla 23931: エラー: ローカル変数
this
を非スコープメンバ関数this.this()
で参照しています - Bugzilla 23948:
__FILE__
および__MODULE__
は、デフォルトのパラメータとしてconst(char)*
に暗黙的に変換することはできません - Bugzilla 23971: C++リンケージを持つスライスを返そうとする際のエラーメッセージをより明確にする
- Bugzilla 24000:
Error: matching } expected, not End of File
で "{" の位置を表示する - Bugzilla 24023: エラーメッセージのタイプに不要なモジュール接頭辞
ランタイム変更点
変更点
- druntime に Linux input ヘッダの翻訳が追加されました。
- ガベージコレクタに Valgrind memcheck ツールの統合が追加されました。
改善点
- Bugzilla 23980: OpenBSD:
unistd.d
にgetthrname(2)
とsetthrname(2)
を追加します - Bugzilla 24044: 配列の比較に
float opCmp(...)
の形式をサポートします(訳注:戻り値がint
ではなくfloat
でも良くなるということです)
ライブラリ変更点
変更点
-
std.algorithm.iteration.permutations
のstatic assert
メッセージが改善されました。 -
std.system.instructionSetArchitecture
とstd.system.ISA
が追加されました。
改善点
- Bugzilla 23881:
std.system
がシステムアーキテクチャに関する機能を持っていない - Bugzilla 23922:
std.socket
の説明リンクを修正
DUB 変更点
- CLIフラグとして
--d-versions
が利用可能になりました。
注目トピック
言語・コンパイラ変更点
alias this
に代入スタイルの構文が許可されるようになりました。
alias this
での宣言が、他のalias宣言と同様の new = old;
形式で書けるようになりました。
struct S
{
int n;
alias this = n;
// 以下と同等:
//alias n this;
}
元々の構文は alias a b;
で b
が新しく a
のように使える、というものでした。
alias b = a;
のほうが他の選言や代入の構文と同じなので読みやすい、ということかと思います。
もう全面的にこれで良いのではないかと思うので、ぜひ更新してこれらの機能を使えるようにしていきましょう。
事前定義されたバージョン識別子 VisionOS
が追加されました。
これはAppleがVR/ARデバイスであるVision Proのために開発した新しいオペレーティングシステムです。
宣伝するわけではありませんが、物をつかむ、移動させるなどの操作が直観的に行えるとのこと。
XcodeやUnityでの開発が想定されているようですが、D言語でも開発されることが想定されているということでしょう。すごいですね。(語彙が不足)
ランタイムの変更点
druntime に Linux input ヘッダの翻訳が追加されました。
これらのヘッダは、Linux Input SubsystemのユーザースペースAPIにアクセスを提供します。
このAPIは、キーボード、タッチスクリーン、ゲームコントローラーなどからの入力を読み取るためや、入力デバイスをエミュレートするために使用されます。
これらは現在 core.sys.linux
を通してインポートすることができます:
import core.sys.linux.input; // linux/input.h
import core.sys.linux.input_event_codes; // linux/input-event-codes.h
import core.sys.linux.uinput; // linux/uinput.h
ガベージコレクタに Valgrind memcheck ツールの統合が追加されました。
今回の目玉、Valgrind memcheck連携です。
Valgrindは、主にLinux向けのメモリ管理とスレッドエラーを検出するためのツール集です。
中でも memcheck
は広く利用されているツールで、メモリリークや未初期化メモリへのアクセス、無効なメモリへのアクセスなどの問題を検出できます。
Windowsユーザーだと直接使用する機会は無いかもしれませんが、Linux環境や仮想環境上で開発・テストする場合には非常に有用です。
コンテナ類の運用でお世話になることがあるかもしれません。
今回は、ガベージコレクタをValgrindの memcheck
ツールと統合するコンパイル時オプションとして -debug=VALGRIND
が追加されました。
これが有効になると、GCは、どのメモリアクセス操作が有効で、どれが無効であるかをValgrindに通知します。
この機能によって、安全なメモリ管理(GC)と安全でないメモリ管理(手動)が混合するプログラムのメモリエラーを検知できるようになります。
例:
import core.memory;
void main()
{
auto arr = new int[3];
GC.free(arr.ptr);
arr[1] = 42; // use after free (解放したメモリを再使用しており問題)
}
この機能を使うためには、一部DMDのソースコードを取得し、ガベージコレクタとライフタイムの実装をプログラムのコンパイルに含め、-debug=VALGRIND
のオプションを付けてコンパイルします。(ちょっと手間で試せていません)
git clone -b v2.105.0 --depth=1 https://github.com/dlang/dmd
dmd -g -debug=VALGRIND program.d -Idmd/druntime/src dmd/druntime/src/{core/internal/gc/impl/conservative/gc,rt/lifetime,etc/valgrind/valgrind}.d
valgrind --tool=memcheck ./program
このオプションは、MEMSTOMP
や SENTINEL
などの他のGCデバッグビルドオプションと互換性があるそうです。
LinuxでValgrindがすでに入っている環境なら、dubのユーザーは以下のコマンドで実際の動作を見ることができます。
git clone -b v2.105.0 --depth=1 https://github.com/dlang/dmd
cat >> dub.sdl <<EOF
debugVersions "VALGRIND"
sourceFiles "dmd/druntime/src/core/internal/gc/impl/conservative/gc.d"
sourceFiles "dmd/druntime/src/rt/lifetime.d"
sourceFiles "dmd/druntime/src/etc/valgrind/valgrind.d"
importPaths "dmd/druntime/src"
EOF
dub build
valgrind --tool=memcheck ./program
Valgrind使いの皆様、D言語でもバッチリ使えるということなので是非試してみてはいかがでしょうか?
ライブラリ変更点
std.algorithm.iteration.permutations
の static assert
メッセージが改善されました。
過去にも行われたエラーメッセージの改善の一種です。
これまで、permutations
は、渡された型が使用可能かどうかを確認するためにテンプレート制約を使用していました。
この場合、permutations
を使っているテンプレート制約に違反する型を渡そうとすると、テンプレート制約を満たしていない、という意味のメッセージが出ます。しかしこれは、なぜ使用できないのか、を把握する面で非常に面倒でした。
元々使われていたテンプレート制約は記述が容易で書く側には良いのですが、オーバーロード解決と相性が悪い側面があります。
しかし permutations
ではオーバーロード解決が必要ないので、テンプレート制約で書く必要はなく、表現力豊かなエラーメッセージが出せる static assert
を用いてチェックされるようになった、というのがこの改善です。
std.system.instructionSetArchitecture
と std.system.ISA
が追加されました。
対象システムの命令セットアーキテクチャ(Instruction Set Architecture, ISA)を表す新しい列挙型と確認用の関数が追加されました。
ISAというのは、いわゆるx86, x64, ARMなど、CPUの種類のようなあれのことです。
今回追加された ISA
という列挙型(enum
)には、25種類のアーキテクチャと unknown
が用意されています。あまり馴染みのないアーキテクチャも対象となっているので、興味があれば一度確認してみてください。(webassembly
といった値もあるので、ISAというか実行環境というか、ちょっと微妙ですが)
https://dlang.org/phobos/std_system.html#.ISA
また追加された instructionSetArchitecture
という関数は、対象CPUのISAが実行時にのみ必要とされる場合、例えば以下に示すような人間が読めるメッセージの提供などに使用されることを意図しています。
import std.stdio;
import std.system;
void main()
{
writeln("Hello ", instructionSetArchitecture, " world!");
}
DUB変更点
CLIフラグとして --d-versions
が利用可能になりました。
dub build
などのコマンドで --d-versions=Xyz
を指定することで、Dソースファイルすべてに version = Xyz;
の指定を一律追加できるようになりました。
これはdub.sdl / dub.jsonファイルでのバージョン指定と同様ですが、CLIから直接指定する形となり、ビルドタイプや設定とは独立して指定できます。
これはビルドコマンドで設定できるため、config設定も不要でかなり簡略化されます。
たとえばCIパイプラインのビルドで、以下のようにバージョンを切り替えたりすることが簡単にできます。
dub build --d-versions=V1
dub build --d-versions=V2
他にも非推奨機能を有効にするであるとか、一時的に試したいあれこれ、configを作るほどではない開発者なりのあれこれを実現するために、切り替えの手間が大きくs苦言できる便利な機能だと思います。
非推奨または廃止される機能
今回は、非推奨が2件、廃止が2件で計4件の機能が非推奨および廃止となります。
余り影響のないものが多いですが、一部わかりづらいものもありますので一つ一つ解説していきます。
また今後の予定などは以下のページにまとまっていますので、こちらも参考としてみてください。
非推奨
-
catch
節は、const
またはmutable
な例外のみを取る必要があります。 - 関数は
enum
ストレージクラスの指定ができなくなりました。
削除/エラー
-
extern(C)
関数をオーバーロードすることはエラーになりました。 -
public
メソッドでオーバーロードされたprivate
メソッドへのアクセスは非推奨フェーズが終了しました。
非推奨 : catch
節は、const
または mutable
な例外のみを取る必要があります。
バージョン 2.104 (前回)で、修飾子(const
, immutable
, など)がついた例外型を throw
することは非推奨となりました。
これは同時に immutable
、inout
、shared
として例外を catch
すること も安全ではないという背景があるためです。
(例外が他の参照を通じてまだアクセス可能である可能性があるなど)
今回から、これらの修飾子で例外を catch
する部分が非推奨となります。
auto e = new Exception("first");
try {
throw e;
} catch(immutable Exception ie) { // これは今後エラーとなります。
e.msg = "second"; // 元の例外を書き換える
assert(ie.msg == "first"); // これは失敗します。
}
.\deprecate_catch.d(8): Deprecation: can only catch mutable or const qualified types, not `immutable(Exception)`
エラーを見かけたら、原則として型の修飾を外す必要があるかと思います。
また、今は非推奨の警告ですが、エラーになることを見据えて積極的に外す取り組みを行っていただくと良いと思います。
特に例外オブジェクトを受け取って処理する共通関数がある場合、たとえば引数が const
であれば catch
の部分は修飾不要であり、敷居は高くないと考えられます。
状況に合わせて対応していただければと思います。
削除/エラー : 関数は enum
ストレージクラスの指定ができなくなりました。
以前は、関数宣言に enum
を用いることが可能でしたが、それが特に副作用がなく、戻り値が存在しない場合には auto
ストレージクラスと同等であったため、この書き方は非推奨となりました。
この文法は、enum
をマニフェスト定数(列挙型定数)として扱う場合と混同される可能性があるため、今後はエラーとされます。
enum void f1() { } // エラー
enum f2() { } // エラー
.\error_enum_func.d(4): Error: function cannot have enum storage class
.\error_enum_func.d(5): Error: function cannot have enum storage class
関数でこれらのエラーが出た場合は enum
を取り除き、必要な場合は auto
を使用してください。
void f1() { }
auto f2() { }
正直 auto
で戻り値なしも推論してくれるので、大体の用途はこれ1つでカバーできると思います。ただしコード補完などの性能が若干落ちる恐れがあるので注意は必要です。
削除/エラー : extern(C)
関数をオーバーロードすることはエラーになりました。
バージョン 2.095.0 以降で、モジュール内で同じ関数を複数回定義することは許可されていません。これはリンク時にシンボルの衝突が起きるからです。
引数の型が異なる関数のオーバーロードはもちろん許可されており、D言語が引数の型を含むシンボル名をマングリング(変更)するために動作します。
ただし、いくつかの外部リンク(例: extern(C)
, extern(Windows)
)はこれを行わないため、それらを使ったうえでオーバーロードするとリンク時にシンボルの衝突が起きる可能性があります。
したがって、このような行為は 2.095 のリリースで非推奨とされていました。
この非推奨は今回エラーに変更されました。
修正方法としては、関数に D
または C++
のリンケージを与えるか、オーバーロードごとに独自の関数名を使用してください。
エラーの例:extern(C)
でオーバーロード
extern(C) float square(float x) { return x * x; }
extern(C) double square(double x) { return x * x; }
.\error_extern.d(2): Error: function `error_extern.square` cannot overload `extern(C)` function at .\error_extern.d(1)
.\error_extern.d(2): Error: function `error_extern.square` cannot overload `extern(Windows)` function at .\error_extern.d(1)
修正策1:関数ごとに名前を変える
extern(C) float squaref(float x) { return x * x; }
extern(C) double squared(double x) { return x * x; }
修正策2:リンケージを外し、オーバーロードが使える extern(D)
相当とする
float square(float x) { return x * x; }
double square(double x) { return x * x; }
特に、C言語とのインターフェースが多いプロジェクトでは、思わずこのようなコードが書かれているケースがあるかもしれません。
しかしC言語側から見ると、ある1つの定義が定まらないとリンクできないため、これまでも実害はなかったものと思われます。(これまでも警告は出ていたため)
何か異常が起きることの無いように早期に修正を検討いただければと思います。
削除/エラー : public
メソッドでオーバーロードされた private
メソッドへのアクセスは非推奨フェーズが終了しました。
以前は、private
メソッドが public
メソッドによってオーバーロードされた場合、その private
メソッドを public
としてアクセスすることができました。
バージョン 2.094 から始まった非推奨期間を経て、以下のようなコードは今後エラーになります。
例:
struct Foo
{
private void test(int) { }
public void test(string) { }
}
Foo().test(3);
この例でいうと、Foo().test(3);
は引数の型から private void test(int)
メソッドに解決されていましたが、これが不可能になります。
.\error_visibility_overload.d(6): Error: function `error_visibility_overload2.Foo.test` of type `void(int)` is not accessible from module `error_visibility_overload`
アクセス保護属性に関するものですので、これは守るほかありません。
同パッケージ内で処理することを想定しているのであれば、private
から package
に変換してください。
そうでなければ、元来 public
が正しい保護属性だった可能性もあります。
エラーを見かけたらアクセス範囲に注意しつつ、適切な対応を検討いただければと思います。
まとめ
Valgrindの連携を中心に、alias this
や --d-version
など、利便性を改善する目的の機能強化がいくつか見られました。
これに加えてVisionOSなどの対応も含まれており、将来への機能強化の意気込みが垣間見えるリリースだったと思います。
また、2023/08/29からD言語の国際カンファレンスである DConf2023 がロンドン(オフライン)で行われます。
コア開発者の方も多くが登壇者であるので、大変な中でのリリースだったと思いますが、かなり苦労されたことと思います。
予定通り無事リリースされて次はDConfということで、こちら申し込み不要でオンライン視聴ができます。ぜひ視聴ご検討ください。
個人的には、何十年とコードを書いてきた作者であるWalter Bright氏による「Crafting Self-Evident Code with D」(Dを使って自明なコードを作る)というキーノートが非常に興味深いと思います。
全体スケジュールは以下リンクです。
The #DConf '23 livestreams are scheduled and waiting to go. Find the links at https://t.co/4ldlBeALdi. If you can't join us in London August 29 - September 1, tune in to the livestreams for your #dlang fix!https://t.co/8snTbhbTk5
— D Language (@D_Programming) August 22, 2023
さて、次回は順調にいけば10月、次のリリースでどのような話題が出てくるのか、期待して待ちたいと思います!