はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.110.0
が 2025/03/07 にリリースされました。
今回は最終リリースの 2.109.1 から約9ヶ月空いたリリースで、D言語史上でも遅れに遅れたリリースとなりました。
遅れた背景については色々あるようですが、それも含めて一通りの内容をご紹介していきます。
より細かい内容は下記リンクからChangeLogをご覧ください。
- ChangeLog
また、前回である6月版のリリースまとめは以下になります。
- D言語の更新まとめ 2024年6月版(dmd 2.109.0)
変更点目次
言語・コンパイラ変更点
変更点
-
-preview=fixImmutableConv
でconst(void)[]
からvoid[]
へのコピーが禁止されました。 - インポート式が16進文字列として扱われるようになりました。
- 新しい特性(trait)として
isCOMClass
が追加され、型がCOMクラスかどうかを検出できるようになりました。 -
bool
型の値が0
または1
以外の場合、@safe
ではなくなりました。
改善点
- Bugzilla 20243: 連想配列のキー型で
inout
が適用されるようになります。 - Bugzilla 24135: メンバー関数のオーバーロードが正しく表示されるようになります。
- Bugzilla 24580: ImportC: 属性の後にアセンブリラベルがあると構文エラーになる問題が修正されました。
- Bugzilla 24598: OpenBSDでのコンパイラテストが調整されました。
- Bugzilla 24623: バージョン識別子の
CppRuntime_Clang/Gcc
がCppRuntime_libcxx/libstdcxx
に変更されました。 - Bugzilla 24841:
UTF-16
サロゲートが文字列のエスケープとして使用される際にエラーを示すヒントが追加されます。
ランタイム変更点
改善点
- Bugzilla 20859:
core.sync.rwmutex.ReadWriteMutex.Reader/Writer.tryLock
にタイムアウトを取るオーバーロードを追加しました。 - Bugzilla 24590:
_d_criticalenter2
におけるモジュールコンストラクターのサイクルと共有libphobos2
における不適正命令の問題が解決されました。
Dub変更点
変更点
-
dub.selections.json
ファイルは親ディレクトリも検索されるようになりました。
注目トピック
言語・コンパイラ変更点
-preview=fixImmutableConv
で const(void)[]
から void[]
へのコピーが禁止されました。
データ操作の安全性を高めるため、-preview=fixImmutableConv
スイッチの動作が強化されました。
これは以下のようなケースで、const(void)[]
データに tail const ポインタが含まれる場合、void[]
へのコピーが後から const
データを破壊する可能性があることへの対処です。
void f(int*[] a, const int*[] b)
{
void[] va = a;
const void[] vb = b;
va[] = vb[]; // b のポインタが a にコピーされる
*a[0] = 0; // a を通じて const なデータが変更される
}
今後 -preview=fixImmutableConv
スイッチが有効な場合、const(void)[]
データを va
にコピーすることは許可されなくなります。
実際に試すと以下のようなエラーが発生します。
source\app.d(12,10): Error: cannot copy `const(void)[]` to `void[]`
source\app.d(12,10): Source data has incompatible type qualifier(s)
source\app.d(12,10): Use `cast(void[])` to force copy
インポート式が16進文字列として扱われるようになりました
こちらはバイナリの埋め込みを使いやすくするための強化です。
インポート式は文字列として型付けされていますが、バイナリファイルを埋め込むためにも使用されます。
これまでは、16進文字列として扱うためにキャストが必要でしたが、今回の変更により、 char
以外の整数型の配列に暗黙的に変換されるようになりました。
// 以前はキャストが必要
immutable ubyte[] iconImg = cast(immutable ubyte[]) import("icon.png");
// 今後は暗黙的にubyte配列に変換される
immutable ubyte[] iconImg = import("icon.png");
なお、この機能に出てくる「16進文字列」と呼ばれているものは、dmd 2.108.0 で行われた以下の機能強化に関係するためです。
import
を式として扱う時は元々ただの文字列として扱われていましたが、今回から x"41424344"
のような16進数リテラルとして扱われるようになったということです。
新しい特性(trait)として isCOMClass
が追加され、型がCOMクラスかどうかを検出できるようになりました
Windowsプログラミングにおいて稀に出てくる COM クラスを扱うための機能強化です。
かなり古い印象はあるものの、まだ強化要望があるというのは流石マイクロソフト技術ですね。
今回は、COMクラスであるかどうかを判定するための __traits(isCOMClass, Type)
が追加されました。
また、__traits
はコンパイル時に静的に判定する目的で、実行時には TypeInfo_Class
フラグを使用して判定できます。
サンプルコードとしては以下のようになります。
import std.stdio;
import core.sys.windows.com; // IUnknown, ComObject
class MyCOMobject : ComObject { }
class NotCOMClass { }
void main() {
// traits による判定
writeln(__traits(isCOMClass, IUnknown)); // true
writeln(__traits(isCOMClass, MyCOMobject)); // true
writeln(__traits(isCOMClass, NotCOMClass)); // false
// TypeInfo_Class による判定
auto obj = new MyCOMobject(); // 簡単のため new で生成
TypeInfo_Class t1 = typeid(obj);
writeln(t1.m_flags & TypeInfo_Class.ClassFlags.isCOMclass); // 1
auto obj2 = new NotCOMClass();
TypeInfo_Class t2 = typeid(obj2);
writeln(t2.m_flags & TypeInfo_Class.ClassFlags.isCOMclass); // 0
}
使い道は色々あると思いますが、個人的にはテンプレート引数のクラスがCOMクラスならエラーにする、といった使い方しか浮かびませんでした。
もし実務でCOMを触る方や、これを使って何か面白いことを思いついた方がいればぜひ共有してください。
ランタイム変更点
core.sync.rwmutex.ReadWriteMutex.Reader/Writer.tryLock
にタイムアウトを取るオーバーロードを追加しました。
core.sync
モジュールは、スレッド間同期の仕組みを提供するモジュールです。
この中にある ReadWriteMutex
は、複数スレッドからデータの読み取りと書き込みを行う時に読み取り同士は排他しなくても良いことに着目して、パフォーマンスを上げようというものです。(ほとんどが読み取り、稀に書き込み、というバランスで特に有効)
追加されたメソッドは以下のようなシグネチャのものです。
@trusted bool tryLock(Duration timeout);
shared @trusted bool tryLock(Duration timeout);
使ってみると以下のようになります。
import core.sync.rwmutex;
import core.time;
void main()
{
auto rwm = new ReadWriteMutex();
// ロックを取る時にタイムアウトを指定
// trueの時はロックできた状態、falseの時はタイムアウト
if (rwm.reader.tryLock(1.seconds)) {
scope (exit) rwm.reader.unlock();
writeln("Reader locked");
} else {
writeln("Reader lock failed");
}
}
Dub変更点
dub.selections.json
ファイルは親ディレクトリも検索されるようになりました。
dub.selections.json
ファイルは、プロジェクトの依存関係を管理するためのファイルです。
このファイルは通常、プロジェクトのルートディレクトリに配置されますが、今回の変更により、親ディレクトリにも配置できるようになりました。
つまり、複数のプロジェクトを含むリポジトリに対して、共通の dub.selections.json
ファイルを配置することができるようになったということです。
この共通で使うファイルには inheritable: true
というフラグを付けておくことで、そのリポジトリ内のすべてのプロジェクトに適用されるようになります。
ただし、各プロジェクトでローカルの dub.selections.json
ファイルがある場合は、そのファイルが優先されます。
また、 inheritable: true
が付いているファイルは、ネストされたプロジェクトで dub upgrade
などのコマンドを実行しても更新されません。
この機能によって、サブパッケージをたくさん持つプロジェクトの場合、一部のパッケージだけ更新してしまって壊れるような事態が防げます。
大きめのライブラリ(たとえば vibe.d など)では嬉しい機能ですね。
私もサンプルプロジェクトを色々書くことがあるので、ちょっと使ってみようかと思います。
非推奨または廃止される機能
今回は、非推奨化が1件(実質複数?)、廃止はありませんでした。
また今後の予定などは以下のページにまとまっていますので、こちらも参考としてみてください。
非推奨
bool
型の値が 0
または 1
以外の場合、@safe
ではなくなりました
bool
型は 0
か 1
で表される真偽値型ですが、これまでは @safe
で 0
または 1
以外の値を bool
として扱うことができました。
しかし、今回の変更により、bool
型の値が 0
または 1
以外の場合、@safe
でないとみなされ警告が出るようになります。
もう少し言えば、「0
か 1
だと分からない場合に警告が出る」ということです。
具体的には以下のようなケースが @safe
コードで非推奨となりました。
-
void
でのbool
の初期化(2.109 から) -
union
からのbool
フィールドの読み取り(2.109 から) - 動的配列から
bool
の動的配列への実行時キャスト -
bool
の動的配列から末尾可変動的配列への実行時キャスト - ポインタを
bool
ポインタ型にキャストする -
bool
ポインタを末尾可変ポインタ型にキャストする
確認できた範囲で、サンプルコードは以下のようなものです。末尾可変配列はどのような型かよくわからず、色々試しましたが警告が出ませんでした。
union U {
bool b;
int i;
}
void test(U u, int* p) @safe
{
bool b1 = void; // void での bool 初期化
bool b = u.b; // union からの bool フィールドの読み取り
int[] a = [1, 2, 3]; // 動的配列から bool の動的配列への実行時キャスト
bool[] b3 = cast(bool[]) a;
bool* b6 = cast(bool*) p; // ポインタを bool ポインタ型にキャストする
}
それぞれ、以下のような警告が表示されるようになります。
source\app.d(40,10): Deprecation: a `bool` must be 0 or 1, so void intializing it is not allowed in safe functions
source\app.d(42,11): Deprecation: cannot access overlapped field `U.b` with unsafe bit patterns in `@safe` code
source\app.d(42,11): Deprecation: cannot access overlapped field `U.b` with unsafe bit patterns in `@safe` code
source\app.d(45,14): Deprecation: cast from `int[]` to `bool[]` not allowed in safe code
source\app.d(45,14): Source element may have bytes which are not 0 or 1
source\app.d(47,13): Deprecation: cast from `int*` to `bool*` not allowed in safe code
source\app.d(47,13): Source element may have bytes which are not 0 or 1
警告なのでまだ無視できますが、修正する場合はいくつかの方法が考えられます。
たとえば、 @safe
をやめて適切なアサーションと共に @trusted
にするか、 cast
を使わずに明示的に変換するなどです。
ややトリッキーなものも含めて、警告が出ないようにする対処をいくつか書いてみます。
void test2(U u, int* p) @safe
{
bool b1 = 0; // 0 または 1 で初期化
bool b2 = u.i != 0; // 0 かどうかを明示的に判定(0 または 1 以外のビットパターンが 1 になる)
int[] a = [0, 1, 0];
import std.algorithm : all;
assert(a.all!(x => x == 0 || x == 1)); // 0 または 1 以外が含まれていないことをチェック
bool[] b3 = (() @trusted { return cast(bool[]) a; })(); // @trusted で警告を消す
assert(*p == 0 || *p == 1); // 0 または 1 であることをチェック
bool* b6 = (() @trusted { return cast(bool*) p; })(); // @trusted で警告を消す
}
その他
リリースの遅れについて
さて、今回の記事は前回からなんと約9ヵ月空いています。当初のリリース目標は 2024年8月1日 でした。
このリリースが遅れに遅れた問題はフォーラムでも質問されたりしているのですが、説明された理由としては2つあるようです。
- リリース作業を担当するメンテナーの多忙
- GitHubへの移行に伴うChangeLog生成スクリプトの改修と混乱
一番影響の大きかった要素は、リリース作業を担当するメンテナの多忙、作業の属人性によるものです。
これについて2024年11月末に以下のフォーラムでアナウンス、担当者の仕事が落ち着くのは恐らく来年1月だ、といった話がありました。そして今回なんとか3月にリリースできた、という状況です。
要因の2点目は、Bugzillaで管理していたIssueをGitHubへの移行するプロジェクトの影響です。
2024年8~10月にはコンパイラリリース、2024年12月にIssueの移行、となるはずがリリースの遅れでリリース関連作業と衝突し、ChangeLogを生成する周りのスクリプトの問題が見つかった、という話のようです。
これまで20年近く開発されてきたD言語ですが、ここまでの問題は初めてであり、ボトルネックの特定と再発防止に取り組むとのことです。
GitHubでリリースも組めるので、より健全な開発体制になることを期待したいですね。
何か大きな問題が起きているときは他の作業も見直す方が安全、ということで。
まとめ
しっかりと機能強化や安全性強化が行われているのですが、規模は小さめの印象でした。
おかげで検証も比較的サクッとできましたが、その分次のリリース(2.111.0)と考えられるNightlyが大変なボリュームになっています。まぁそれは次の機会に…
というわけで久しぶりのリリースでした!強化されたポイントをぜひ活用してみてください!
次のリリースも引き続き注目していきましょう!