はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.112.0 が 2026/01/07 にリリースされました。
今回もmacOSのRegression騒ぎやらで色々あったようで、前回リリースの 2.111.0 からかなりの時間を要しました。
またGitHubのIssueに移行したためか、Bug fixとEnhancementが区別されなくなっているようです。分類困難だったのでそのまま掲載しており、数が膨大なところありますがご容赦を。
ざっくり把握したい場合は最下部のまとめだけ読んでください。
以下、注目の変更点や非推奨事項及び対処などをまとめていきます。
詳細は ChangeLog をご覧ください。
- ChangeLog
前回のリリース記事はこちらです。
- D言語の更新まとめ 2025年4月版(dmd 2.111.0)
変更点目次
言語・コンパイラ変更点
変更点
- 連想配列操作は druntime のテンプレート実装へ lowering されるようになりました(訳注: lowering = コンパイラ内部で汎用実装へ変換すること)
-
auto ref戻り値はautoとrefを隣接して記述する必要があります - ビットフィールドが標準化され、
-preview=bitfieldsが不要になりました - ぶら下がった
elseはエラーになります -
-extern-std=c++23を受け付けるようになりました - 外部インポートパススイッチ
-extIが追加されました - C ファイルで
__moduleによる module 宣言が可能になりました -
int op= float代入の暗黙整数変換が非推奨になりました(浮動小数から整数への切り捨てが起きるため)
バグ修正/改善点
- GitHub 17462:
object.requireが値代入前に AA を変更してしまう - GitHub 17481:
rt.minfo sortCtorでメモリリーク - GitHub 17804: 連想配列イニシャライザではない
- GitHub 18018: AA 代入を跨ぐ
gotoが失敗し誤ったエラーになる - GitHub 18101: ビットフィールドの
?:代入で誤ったコード生成 - GitHub 18127: ImportC: 別翻訳単位での
struct再宣言の互換性チェック不足 - GitHub 18247: 64-bit ビットフィールドが常に
0 - GitHub 18263: DMD が空白を含むパスにあると ImportC が失敗
- GitHub 18614: インポートモジュール内
structのRTInfo生成に失敗する - GitHub 18950: CodeView:
ref戻り値のデバッグ情報が不正 - GitHub 19587: 簡単なコードブロックに行情報がない
- GitHub 19788: パラメータが関数 UDA を継承する
- GitHub 19983: ImportC の関数再宣言は関数スコープで許可されるべき
- GitHub 20075:
immutableで__ctorエラーメッセージが逆 - GitHub 20092: importC: 一部の compound literal のアドレスが取れない
- GitHub 20114:
in演算子での AA がstatic ifで動かない - GitHub 20157: ImportC: C
structフィールド初期化で designator が 1 つしか使えない - GitHub 20184: ImportC が
.i/.cを認識するが.hを認識しない - GitHub 20301: "The CodeView record is corrupted" の再現困難な不具合
- GitHub 20318: mutable への暗黙変換が失敗する理由を説明すべき
- GitHub 20334: ImportC: 文字列リテラル
#define由来enumがconst(char)*に暗黙変換できない - GitHub 20365: -preview=bitfields:
refでビットフィールドのアドレスが逃げる - GitHub 20473: ビットフィールドとスライス/クラス参照を併せ持つ
structがコンパイル不可 - GitHub 20499: ImportC: ポインタ名の
typedef structがstruct名で使えない - GitHub 20502: importc: マクロと同名の
structが衝突する - GitHub 20855:
-vでインポート元を表示すべき - GitHub 20901: dip1000: インデックス配列リテラルでスタックポインタが逃げる
- GitHub 20917:
__trait(compiles)内のクロージャと@nogc併用で@nogcチェックが失敗 - GitHub 20985:
runnable\\exe1.cが Win64 で不安定 - GitHub 21024:
x^^0/x^^1/x^^2式の最適化 - GitHub 21033:
-profile=gcで簡単なプログラムがリンクできない(DMD ICE) - GitHub 21052:
enum変数初期化の@nogcチェックが欠落 - GitHub 21054: init symbol からスライスした配列リテラルに位置情報がない
- GitHub 21068: 隠れた基底クラスコンストラクタ呼び出しメッセージが誤解を招く
- GitHub 21098:
importだけのモジュールがコンパイルできない - GitHub 21105:
is式で不必要な lowering のコード生成が行われる - GitHub 21126: macOS 15.4 で
main前にクラッシュする - GitHub 21142: 浮動小数点コードで segfault
- GitHub 21150: ImportC:
_Alignofではなく整列値を要求する - GitHub 21153:
isAliasThisTupleで無限ループ - GitHub 21161:
placement newが default-initstructで失敗 - GitHub 21179:
cast後にconst(T)からTへの変換に失敗 - GitHub 21189: regular file でない場合にエラー行が欠落/誤り
- GitHub 21203:
placement newが「初期化」として扱われない - GitHub 21207:
enumAA のデフォルト引数で dmd がクラッシュ - GitHub 21210: ImportC: 配列を含む
structの= {0}初期化が失敗 - GitHub 21215:
structリテラルの "not a member" エラー位置が不正 - GitHub 21225: ImportC: マクロが
enumと解釈され関数と衝突する - GitHub 21241: ImportC: 異なる C import にある同名
static関数で誤った関数が呼ばれる - GitHub 21247: CTFE 実行で DMD がハングする
- GitHub 21258: Phobos 3 のビルドが最近の変更で壊れている
- GitHub 21259: シーケンス代入時の "not an lvalue" エラーが不正
- GitHub 21267: ImportC:
__forceinlineが無視される - GitHub 21271: ImportC:
__pragma互換マクロが不正 - GitHub 21280:
__NR_getrandomを追加 - GitHub 21298: [iasmgcc] 命令テンプレートの文字列欠落が診断されないことがある
- GitHub 21303:
sizeof == 0のstructでデストラクタが呼ばれない - GitHub 21304: 無効配列初期化時の "cannot use array to initialize
_error_" が冗長 - GitHub 21317: エラーメッセージの位置が不正
- GitHub 21382: CodeView:
final class関数が vtbl 上でvirtualと表示される - GitHub 21384: CodeView: デバッガで関数引数の順序が誤って表示される
- GitHub 21403: 関数契約の不正な構文でコンパイラが segfault
- GitHub 21406:
debug条件付きヘッダ生成でクラッシュする(Regression 2.111) - GitHub 21408: 推論された
opEqualsが再帰的な配列比較で分かりづらいエラーになる - GitHub 21414:
__rvalueは@system必須 - GitHub 21416:
ref関数呼び出しで__rvalueが無視される - GitHub 21426: Windows GC の並列スキャンで 1 スレッドしか起きない
- GitHub 21429:
mixintemplates で複数mixinのopDispatch/opBinaryが生成できない - GitHub 21435: 最適化付きで dmd をビルドするとコード生成が不正
- GitHub 21452:
substInoutが配列のenumbase type に変換してしまう - GitHub 21456: 連想配列
foreachで key/value の型不整合が許容される - GitHub 21476: move constructor があると overload error になる
- GitHub 21478: copy constructor を実装した
structでrefパラメータ overload が呼ばれない - GitHub 21479:
-inlineフラグでcast式がコンパイルできない - GitHub 21504:
__traits(compiles)が@nogc推論に影響する - GitHub 21523:
pragma(inline, true)付き関数をテンプレートがインライン化しない - GitHub 21549: コンパイラがクラッシュ(コード生成の問題の可能性)
- GitHub 21562:
void[]を埋めようとすると dmd が segfault - GitHub 21576: 短縮コンストラクタで "need 'this' to access member" エラー
- GitHub 21605: 誤ったテスト: bug19652
- GitHub 21615: [Regression 2.110] 配列
length代入が後続ページに拡張されない - GitHub 21619: ICE
core.exception.AssertError@src/dmd/pragmasem.d(456): Assertion failure - GitHub 21630: runtime foreach の loop 変数で
enumとaliasが無視される - GitHub 21646:
opCast無効 +-checkaction=contextでコンパイルエラー回帰 - GitHub 21660: D ビットフィールド初期化の重複エラー
- GitHub 21663: ビットフィールドが DWARF デバッグ情報に含まれない
- GitHub 21665: CodeView のビットフィールドデバッグ情報が不正
- GitHub 21679: ImportC: GCC IASM で C のトークンを処理
- GitHub 21690: 多次元 AA の静的初期化で ICE
- GitHub 21693:
__rvalueがコンポーネント式で無視される - GitHub 21718: importC:
_Alignas指定子で整列を下げられない - GitHub 21740: ImportC: UCN が無効文字を識別子の一部として受理する
- GitHub 21744: ImportC: 4 桁 UCN 識別子
\\unnnnが 8 桁\\Unnnnnnnnと一致しない - GitHub 21745: ImportC:
__check()式で参照された__func__が生成されない - GitHub 21757: 32-bit
debugビルドで MS Linker が失敗(stack overflow/LNK1318) - GitHub 21774:
resolveAddressesWithAtosが PID をリークし FD 枯渇につながる - GitHub 21813: ImportC が
_declspec(deprecated)で落ちる - GitHub 21874: primitive/
structの non-array typesafe variadic を引数なしで呼ぶとコンパイルエラーになる - GitHub 21945: [REG] DIP1000 scope array of arrays 比較で不自然なエラーになる
- GitHub 22323: [Regression master] AA を含む
structリテラルで "cannot get frame pointer" エラー
ランタイム変更点
変更点
-
_d_arraysetlengthTがテンプレート化され、TypeInfo依存が削除されました -
_d_arrayappendcTXランタイムフックがテンプレート化されました -
_d_arraysetcapacityランタイムフックがテンプレート化されました -
core.int128に 64-bit 用のmulとudivmodオーバーロードが追加されました - macOS 15.4 で生成バイナリがクラッシュする問題が修正されました
- druntime の C マクロ翻訳がテンプレートへ置き換えられました
バグ修正/改善点
- ChangeLog にはランタイムの項目無し
ライブラリ変更点
変更点
-
std.algorithm.iterationにlazyCacheが追加されました -
getrandom()の後方互換 shim が追加されました - 内部マルチバックエンドのエントロピー取得機構が追加されました
-
std.uniが Unicode 16.0.0 から 17.0.0 に更新されました -
std.uuidに uuid v7 対応が追加されました -
std.convにwriteText/writeWText/writeDTextが追加されました
バグ修正/改善点
- GitHub 9829:
doCount=falseでもstd.conv.parse!int,stringが count してしまう - GitHub 10440:
eachの template constraint が広すぎる - GitHub 10491:
Complex!float.abs/hypotが小さい引数で不正な結果 - GitHub 10506:
std.string.wrapの列数カウント方針が不明確 - GitHub 10513:
powmodのulong実装が非常に遅い - GitHub 10540:
sumtype.dの "degrees" 変数名を(少なくとも kelvin case で)修正 - GitHub 10550:
formattedWriteが文字列補間をサポートすべき - GitHub 10577: [std.math]
std.math.algebraic.cbrtがpureではない - GitHub 10701:
randomUUIDドキュメントのリンク切れ - GitHub 10731: v2.111 で
std.random.uniform()による Windows リンクエラー - GitHub 10742:
std.path.withExtensionがconst非互換レンジで動作しない - GitHub 10775: TZDIR を尊重する
- GitHub 10801:
sgnGamma(-0.5)は-1であるべき - GitHub 10802:
digammaの0の扱いがgammaと不一致 - GitHub 10811: [REG2.111.0] move constructor 付き
structでRedBlackTreeがコンパイルエラー - GitHub 10840:
writelnがビットフィールドの表示をサポートしない
Dub変更点
変更点
-
--destビルドオプションが追加されました - dubファイル(訳注: dub.sdl/dub.json)に
frameworksキーが追加されました
バグ修正/改善点
- ChangeLog には dub の項目無し
ドキュメント変更点
変更点
- ChangeLog には ドキュメントの変更点なし
バグ修正/改善点
- dlang.org GitHub 4184: copy constructor の使用は警告し、postblit constructor の使用は警告しないよう spec が修正
- dlang.org GitHub 4191:
mixin(__FUNCTION__)の使用を警告 - dlang.org GitHub 4217: EqualExpression が
classのnullを扱えないと誤記されている - dlang.org GitHub 4222: 比較ドキュメントの文言が誤り
- dlang.org GitHub 4224: CmpExpression が
classのnullを扱えないと誤記されている - dlang.org GitHub 4234:
structの rvalue に対するメソッド呼び出しが可能であることを明記 - dlang.org GitHub 4251: 連想配列
foreachの key 型はconst変換可能ならrefが使える - dlang.org GitHub 4260: 型仕様に
voidの節を追加 - dlang.org GitHub 4266: "report a spec bug" が bugzilla を指している問題
- dlang.org GitHub 4267: 2 種類の ct
foreach文の説明を明確化 - dlang.org GitHub 4305: ショップが非表示
注目トピック
言語・コンパイラ変更点
連想配列操作は druntime のテンプレート実装へ lowering されるようになりました
今回の目玉機能です。
連想配列操作が druntime 側のテンプレート実装(core.internal.newaa)へ lowering されるようになりました。
これにより実行時型情報(TypeInfo)への依存を減らし、より最適化が期待できるようになります。
ここで言うloweringというのは、コンパイラ内部で高レベルの構文や操作を別の実装形へ置き換えることのことです。たとえば配列を複数連結するような操作(arr1 ~arr2 ~ arr3)は、core.internal.array.concatenation というモジュールの _d_arraycatnTX(arr1, arr2, arr3) という呼び出しに置き換えられたりします。
これまで連想配列の操作は、実行時型情報を使用したある程度動的な処理になっていました。
今回からコンパイラが連想配列の操作をテンプレート実装へ展開する方式に変わるので、静的に型情報を活用できるようになり、更なるコードの最適化が期待できるようになります。
日常的なコードで影響が出るケースは少ないと思いますが、内部的にはかなり大きな変更です。
影響が出るケースもあるかと思いますので、細かい最適化を期待するランタイム構成では注意が必要です。
ビットフィールドが標準化され、-preview=bitfields が不要になりました
-preview=bitfields が不要になり、ビットフィールドが標準機能として取り込まれました。
ビットフィールドは元々C言語の機能で、構造体のメンバーにビット幅を指定してレイアウトをぎゅっと詰めて保持する機能です。(例: uint a:3; uint b:5; のように宣言すると全体8ビットに収まる)
これまでは -preview=bitfields を付けたときのみ有効で、使用する側はコンパイルオプションに依存していました。
今回からは標準機能として常時有効になるので、-preview=bitfields 指定を気にせず使えるようになります。
既存のビットフィールド利用コードはそのまま動くので、ほぼ影響はないと思います。
Cのビットフィールドを使うコードがほぼそのまま移植できる言語というのは結構珍しいと思うので、機会があればぜひ試してみてください。
-extern-std=c++23 のサポート
C++連携強化として、-extern-std=c++23 が指定できるようになりました。過去には -extern-std=c++20 の追加やデフォルトC++11化も行われており、今回も定期的なC++連携を強化していく流れの一環と言えます。
-extern-std は、DからC++の外部リンケージを扱う際に、対象とするC++標準(extern(C++) でリンクするライブラリの言語標準)を明示するためのスイッチです。これはコンパイラ内部のターゲット情報や条件付きコンパイルの判断材料として使われます。
これまでは c++23 を指定しても受け付けられず、__traits(getTargetInfo, "cppStd") に反映されませんでした。
今回からは c++23 が認識され、__traits(getTargetInfo, "cppStd") の値が 202302 に更新されます。
現時点でこれを使った実装は多くないため、実動作への影響は限られます。
コード生成の挙動そのものは限定的と思われますが、C++23と連携させるプロジェクトであれば指定を検討すると良いかもしれません。
"""
外部インポートパススイッチ -extI が追加されました
Windows環境でDLLを利用する際の参照定義について、特定のモジュールを「現在ビルドしているバイナリの外部にあるもの」として明示的に扱うための -extI オプションが追加されました。
-extI はインポート先の探索パスを増やす -I オプションと同様の一種ですが、そこで見つかったモジュールを「外部モジュール」として扱います。たとえば -extI=vendor/ のように指定すると、vendor/ 配下から見つかったモジュールを外部モジュールとして区別できます。
典型的には、DLLとセットで提供される宣言用ファイル(.di)をこのようなディレクトリに配置しておき、-dllimport=... オプションとセットで使用します。
こちら調査したら大変ややこしかったので、以下確認した最低限の前提と従来の課題、-extI による改善点および基本的な使い方をまとめます。
-
最低限の前提知識
- Windowsでは、実行時にDLLを読み込むことを前提として、
.libファイルをリンク時に指定し、実行時に解決する仕組みがあります。(DLLImportおよび暗黙リンクと呼ばれる) - この時、DLL側で定義された関数や変数を利用側から参照する場合、それらが「DLLから提供される外部シンボルである」ことをコンパイラやリンカに適切に伝える必要があります。(そうしないと後で解決されるということが分からず、単に未実装の宣言と区別が付かないため)
- これはD言語において、「外部から提供される宣言」には
externを付け、「外部へ公開する定義」にはexportを付けることで制御します
- Windowsでは、実行時にDLLを読み込むことを前提として、
-
従来の課題
- DLL側と利用側で宣言/定義の対応や
export/externの付け方が揃っていないと、意図した dllImport / dllexport の扱いにならず、コンパイルやリンクで問題が発生する - 従来この問題の対処として
-dllimport=allという機能があったが、これを使うと広範囲のシンボルがDllImportとして扱われ、意図しない不整合を招くことがあった
- DLL側と利用側で宣言/定義の対応や
-
-extIの効果-
-extIは「外部モジュールとして扱うimportパス」を指定し、そのパスから見つかったモジュールを外部モジュールとしてマーキングする - これを
-dllimport=externalOnlyと併用すると、外部モジュールとしてマーキングされたモジュールに由来するシンボルのみをDllImportとして扱うように制御できる- 結果として、自前コードへの影響を抑えつつ外部DLL由来のシンボルだけを
DLLImportの対象にできる
- 結果として、自前コードへの影響を抑えつつ外部DLL由来のシンボルだけを
-
-
使い方(基本)
- DLLとセットで配布される宣言用ファイル(通常は
.di)を、-extIで指定したディレクトリに配置する - DLLを利用するEXE側のビルド時に、
-extI=...と-dllimport=externalOnly(または要件に応じたモード)を付与する - 併せて、リンク時に DLL に対応する import library(
.lib)を指定する。(一般的な暗黙リンク構成の場合)
- DLLとセットで配布される宣言用ファイル(通常は
ImportC の __module 対応
C言語との連携強化の一環で、ImportCの時にCソースで「Dから参照する時のモジュール名」を __module <モジュール名> で宣言できるようになりました。
__module 宣言がない場合は従来通りファイル名ベース(ディレクトリは無視)でモジュール名が決定されます。つまり同じ名前だと衝突して読み込めませんでした。
__module 宣言を使うと、Cソース内で明示的にモジュール名を指定できるため、同名ファイルの衝突を避けられます。また、ビルド構成の変更でモジュール名を変える必要がなく、開発が安定します。
サンプルコードが分かりやすいと思うので、以下に示します。書き方はほぼテンプレだと思って良さそうです。
alpha/config.c
#if __IMPORTC__
__module demo.alpha;
#endif
int value() { return 10; }
beta/config.c
#if __IMPORTC__
__module demo.beta;
#endif
int value() { return 20; }
gamma/config.c
int value() { return 30; }
main.d
import demo.alpha : alphaValue = value; // alpha/config.c を demo.alpha でインポート
import demo.beta : betaValue = value; // beta/config.c を demo.beta でインポート
import config : gammaValue = value; // gamma/config.c を config でインポート
assert(alphaValue() == 10);
assert(betaValue() == 20);
assert(gammaValue() == 30);
ランタイム変更点
_d_arraysetlengthT がテンプレート化され、TypeInfo 依存が削除されました
今回のランタイム内部最適化の一つです。動的配列の長さ変更に関わるランタイムフックとして _d_arraysetlengthT がテンプレート化され、TypeInfo 依存が削除されました。
ランタイムフックというのは、コンパイラが生成するコードから呼び出されるランタイムライブラリ内の関数のことです。たとえば動的配列の長さ変更を行うとき、コンパイラは arr.length = N; を _d_arraysetlengthT(arr, N); というランタイムフックに置き換えてコード生成します。
これは難しいコードをコンパイラが直接生成するのではなく、ある程度汎用性のある関数に抽象化することでコード生成の爆発を防ぐ意図があります。
今回ターゲットとなる _d_arraysetlengthT ではこれまで実行時型情報を取る typeid および TypeInfo を介してサイズ確認などの処理を行っていましたが、今回の変更でテンプレート化されたので静的に型情報を活用できるようになりました。
TypeInfo はクラスでGC依存があるので、その依存が無くなると BetterC モードでの定義の取り扱いが改善するほか、細かい属性推論や最適化など幅広い効果が期待できます。
以前はコンパイルエラーになっていたコードが今は通るようになっているかもしれませんし、速度も改善しているかもしれません。
興味のある方はぜひ試してみてください。
_d_arrayappendcTX ランタイムフックがテンプレート化されました
今回のランタイム内部最適化の一つです。上記の動的配列サイズ変更の類似で、動的配列の要素追加に関わるランタイムフックである _d_arrayappendcTX がテンプレート化されました。
1点注意すべきであるのは、こちらは先ほどの例と違って TypeInfo 依存が削除されない、ということです。
じゃあなぜテンプレート化したのかというと、主に属性推論のためです。
細かい論点はProposal.pdfを見ていただきたいのですが、このappendに関しては以下2つの課題を解決します。
- 書いた側は
@safe pure nothrowなスコープでも通ってしまう一方で、実際に呼ばれるランタイムフックの実装が@safe/pure/nothrowでない可能性がある - テンプレート化は、ランタイムを「使った分だけリンクされる」方向(pay for what you use)に近づける助けとなる
単に高速化というだけでなく、より安全で効率的なコード生成を目指した変更と言えます。
配列の連結も使用頻度はとても多いと思われるので、ぜひ活用していきたいですね。
_d_arraysetcapacity ランタイムフックがテンプレート化されました
今回のランタイム内部最適化の一つ、他2件と同様に動的配列の容量調整に使われるランタイムフック _d_arraysetcapacity がテンプレート化されました。
動的配列の capacity は確保済みの要素数(バッファの大きさ)を表し、例えば次のような容量変更は _d_arraysetcapacity を経由したコードになります。
int[] a;
a.capacity = 16;
変更前は TypeInfo(実行時型情報)を受け取り、要素サイズなどの情報を実行時に参照していました。変更後はテンプレート化で型が静的に分かるようになり、TypeInfo 依存が削除されています。
TypeInfo を避けたい -betterC 構成ではメリットがあるので、一度試してみると良いかもしれません。
core.int128 に 64-bit 用の mul と udivmod オーバーロードが追加されました
今回のランタイム内部最適化の一つで、core.int128 に 64-bit オペランド向けの mul と udivmod オーバーロードが追加されました。core.int128 は 128-bit 整数演算の補助モジュールで、Cent 型や演算ヘルパーを提供します。
mul は 64-bit 同士の乗算から 128-bit の積を返し、udivmod は 128-bit の値を 64-bit で割った商と剰余を同時に求めます。使い方は次の通りです。
import core.int128;
ulong a = 3;
ulong b = 5;
Cent product = mul(a, b);
ulong mod;
ulong q = udivmod(product, 7, mod);
変更前は 64-bit オペランドに対するオーバーロードがなく、mul / udivmod を使うには 128-bit 型に明示的に拡張する必要がありました。変更後は 64-bit から直接呼べるようになり、利便性が向上しています。
また、x86_64 ではこれらの演算が単一命令に対応するため inline asm で最適化されています。高価は 64-bit から 128-bit の積や除算・剰余を扱う箇所に限られますが、該当コードがある場合は core.int128 の利用を検討すると良いと思います。
macOS 15.4 で生成バイナリがクラッシュする問題が修正されました
回帰の不具合修正です。今回のリリースが遅れる原因となった修正でもあります。
macOS 15.4 で生成バイナリが起動時にクラッシュする問題があり、それが修正されました。対象はDRuntimeを使う通常のバイナリで、TLSセクション(スレッドローカル変数を配置する領域)の初期化が関係します。
たとえば次のように TLS 変数を持つ最小コードでも、起動直後にクラッシュするケースがありました。
import std.stdio;
int tlsValue;
void main()
{
tlsValue = 1;
writeln(tlsValue);
}
修正はランタイム側で完結しており、過去の macOS との互換性も維持されているため特に対応は必要ありません。
アプリが落ちるような現象に遭遇していた場合、このアップデートにより改善するかもしれません。検討してみてください。
druntime の C マクロ翻訳がテンプレートへ置き換えられました
BetterCに関連する互換性改善です。druntime にある C マクロ翻訳(C の #define を D から使える形にした定義)が、関数ではなくテンプレートとして提供されるようになりました。-betterC は DRuntime に依存しない構成でビルドするモードとなっており、GCなどのランタイム依存を排除し、組み込み等に向く設定が一式有効化されます。
この時問題になっていたマクロ翻訳の代表例が core.sys.posix.sys.wait の WIFEXITED です。
これまでは関数実装がdruntimeにあり、-betterC でリンクしない構成だと、関数のシンボルが解決できずリンクエラーになることがありました。
import core.sys.posix.sys.wait;
import core.sys.posix.unistd;
extern(C) int main()
{
int status;
pid_t pid = vfork();
if (pid == 0)
return 0;
waitpid(pid, &status, 0);
return WIFEXITED(status) ? 0 : 1; // Link error in -betterC
}
Error: undefined reference to `core.sys.posix.sys.wait.WIFEXITED(int)`
referenced from `main`
変更後はテンプレートとして提供されるため、参照された分だけインスタンス化され、-betterC でもリンクエラーになりにくくなりました。影響範囲は C マクロ翻訳を使う -betterC ビルドに限られるので、該当する場合は更新を検討すると良いでしょう。
ライブラリ変更点
std.algorithm.iteration に lazyCache が追加されました
ライブラリの便利関数追加です。lazyCache はレンジの遅延キャッシュで、「高コストな要素生成を必要な分だけ評価しつつ、再評価時の再計算を避ける」を実現します。役割としては、要素が要求されたタイミングで計算し、その結果を内部に保持します。
これまでは cache がありましたが、こちらは「構築時と popFront のタイミングで次要素の front を評価してキャッシュする」方式です。今回追加された lazyCache は、front が要求されるまで評価を遅らせる(lazy)方式になり、必要な要素だけ計算する流れに変わります。
import std.algorithm.iteration : map, cache, lazyCache;
import std.range : iota;
__gshared int calls;
int expensive(int x)
{
++calls;
return x * x;
}
void main()
{
calls = 0;
auto eager = iota(0, 3).map!(a => expensive(a))().cache();
assert(calls == 1); // cache は構築時に front を評価する
eager.popFront();
assert(calls == 2); // popFront で次の front を評価
calls = 0;
auto lazy = iota(0, 3).map!(a => expensive(a))().lazyCache();
assert(calls == 0); // lazyCache は要求されるまで評価しない
lazy.popFront();
assert(calls == 0);
auto first = lazy.front; // ここで初めて評価される
assert(calls == 1);
assert(first == 1);
}
「レンジを列挙するが、直接要素を使うかどうかは不定」という状況では計算を回避できるので試す価値があります。
レンジ外のフラグを見て処理を変えるとか、場合によっては競技プログラミングなどで便利かと思います。
コンパイラが更新されたら、ぜひ試してみてください。代替実装を自分で書いてみるのも良いかもしれません。
getrandom() の後方互換 shim が追加されました
ちょっとした互換性の改善トピックです。
getrandom() は Linux の乱数取得システムコールで、用途は暗号学的に安全な乱数を得るためのものです。古いLinuxで getrandom() が利用できない場合があるので、それに備えた shim(互換層)が追加されました。
これまでは getrandom() が呼べない環境だとエントロピー取得に失敗し、std.random.unpredictableSeed などが動かないケースがありました。変更後は /dev/urandom を使って getrandom() の一部をエミュレートするため、古い環境でも動作が成立しやすくなります。
有効化はビルド時で、LINUX_LEGACY_EMULATE_GETRANDOM=1 を付けて DMD/Phobos をビルドする必要があります。たとえば次のようなコードが対象で、古い環境での動作が改善されます。
import std.random : unpredictableSeed;
import std.stdio;
void main()
{
writeln(unpredictableSeed);
}
ビルドフラグを立ててビルドするような手間のかかる話であり、基本的に通常利用される方には影響はありません。
内部マルチバックエンドのエントロピー取得機構が追加されました
上記と同様暗号関連の機能追加です。エントロピー取得機構は、暗号学的に安全な乱数の元となるデータを取得する仕組みで、乱数シードや安全な乱数生成の基盤として用意されたものです。今回、内部で複数バックエンドを試行する仕組みが導入されました。
変更後は「利用可能なバックエンドを順に試す」方式になり、Linux では getrandom、BSD では arc4random_buf/getentropy、POSIX では /dev/urandom、Windows では BCryptGenRandom などから選択されます。
特定のOSで getrandom が使えない環境などでは、安定性向上につながる可能性があります。
もし該当する場合はバージョンアップを検討してみてください。
std.uni が Unicode 17.0.0 に更新されました
互換性改善です。std.uni が Unicode 17.0.0 に更新されました。std.uni はD言語におけるUnicode処理の中核で、用途は文字の分類・正規化・ケース変換・書記素境界などを扱うためのものです。
Unicodeは年に1回程度更新され、新しい文字の追加やプロパティ(例: アルファベット判定など)の見直しが行われます。今回は Unicode 16.0.0 から 17.0.0 へ更新されました。仕様の詳細は公式ページを参照してください。
以下は isAlpha(Unicode プロパティに基づくアルファベット判定)を使った簡単な例です。結果の総数が更新に伴って変わることが確認できます。
import std.algorithm : filter;
import std.range : iota, walkLength;
import std.uni : isAlpha;
import std.stdio;
void main()
{
const count = iota(0, dchar.max).filter!isAlpha.walkLength;
writeln(count);
// 2.111.0 (Unicode 16.0.0): 142759
// 2.112.0 (Unicode 17.0.0): 147421
}
多言語テキストや絵文字を扱う場合は最新の規格追従が重要なので、該当するプロジェクトでは更新を検討すると良いかと思います。
std.uuid に uuid v7 対応が追加されました
機能追加です。std.uuid に UUID v7(Unix epoch ミリ秒ベースのタイムスタンプ + 乱数)対応が追加されました。
これは本当にランダムなUUID v4と比べて、「時系列でソート可能なIDを生成する」という特徴を持たせるための規格です。
これまで std.uuid はv7形式を扱えず、時刻ベースのUUIDを生成するには別実装が必要でした。変更後は UUID(SysTime) で v7 のUUIDを生成できるようになり、uuidVersion で v7(timestampRandom)であることも確認できます。
import std.datetime : DateTime, SysTime;
import std.stdio;
import std.uuid : UUID;
void main()
{
SysTime st = DateTime(2025, 8, 19, 10, 38, 45);
UUID u = UUID(st); // v7 生成
writeln(u);
assert(u.uuidVersion == UUID.Version.timestampRandom);
assert(u.v7Timestamp() == st);
}
UUIDがバラバラでランダムアクセスになってしまうケースはよくありますが、これである程度アクセスを局所化するなどの効果が期待できます。もしそのような課題のあるプロジェクトでは使用を検討してみてください。
std.conv に writeText / writeWText / writeDText が追加されました
今回の目玉機能の1つです。
「与えられた引数を文字列にして返す」のではなく、「出力レンジへテキストを書き込む」ための関数としてwriteText / writeWText / writeDText の3つが追加されました。テキストが書き込める出力レンジというのは、いわゆる「Appender!string / appender!string のような、文字列を put できるオブジェクト」のことです。
従来前は text(...) で一度文字列を生成し、それを出力先へ追加する必要がありました。今後は appender などの出力レンジに対して直接書き込めるため、余計な一時文字列を避けられます。これによって効率的な文字列の構築が可能になります。
import std.array : appender;
import std.conv : writeText;
void main()
{
auto output = appender!string();
output.writeText("2 + 2 == ", 4);
assert(output.data == "2 + 2 == 4");
}
大量の文字列連結やログ生成などでは、出力レンジへの直接書き込みが重要になる可能性もあります。
使えるところがないかぜひ検討してみてください。
Dub変更点
--dest ビルドオプションが追加されました
地味ながらも便利そうな機能追加です。
--dest は dub のビルド出力をまとめるための staging 用プレフィックスを指定するオプションで、targetPath と workingDirectory に同じ追加のパスを付ける機能を提供する物です。
DUBプロジェクトの出力先や作業ディレクトリは targetPath と workingDirectory で指定しますが、これを切り替えたい場合は設定ごとに targetPath / workingDirectory を書き分ける必要がありました。変更後は --dest を指定するだけで、設定値にプレフィックスを付与できます。
name "dub-dest-mre"
targetPath "build"
workingDirectory "run"
configuration "featureA" {
versions "FeatureA"
# 以前はここに targetPath / workingDirectory を書く必要があった
}
# 通常
dub run
# -> build/ にバイナリが出力され、run/ が作業ディレクトリになる
# staging 先をまとめる
dub run -c featureA --dest=out/featureA
# -> out/featureA/build/ に出力、out/featureA/run/ が作業ディレクトリになる
dub run -c featureB --dest=out/featureB
# -> out/featureB/build/ に出力、out/featureB/run/ が作業ディレクトリになる
CIで複数パターンの出力先を切り替えたり成果物を分離したい場合に便利ですね。
dubファイルに frameworks キーが追加されました
macOS 連携強化です。frameworks は dub ファイルで macOS フレームワークを宣言するためのキーで、リンク対象のフレームワークを明示することができるようになります。
これまでは、フレームワークを使う場合に lflags "-framework" "Cocoa" のようなリンクフラグを手動で書く必要がありました。これからは frameworks に列挙するだけで済むため、設定が簡潔になり、複数の構成での管理もしやすくなります。
name "dub-frameworks-mre"
targetType "executable"
sourcePaths "source"
frameworks "Cocoa" "OpenGL"
コンパイラ変更点で大きな macOS の問題が修正されたのもあり、macOS 向けの対応は着々と進んでいます。
非推奨または廃止される機能
今回は、非推奨化が2件、エラー化が1件でした。
また今後の予定などは以下のページにまとまっていますので、こちらも参考としてみてください。
非推奨
auto ref 戻り値は auto と ref を隣接して書く必要があります
構文の明確化による非推奨です。auto ref は戻り値に auto と ref を併記する構文で、「参照返しが可能ならする」という意味になります。
以前は ref auto int f() のように順序が逆でも受理されていましたが、今後は auto ref の順のみが許容され、逆順は非推奨になりました。
ref auto int f() => 3; // Deprecation
// 修正例
auto ref int g() => 3;
deprec-auto-ref-return/message.d(3): Deprecation: `auto ref` return type must have `auto` and `ref` adjacent
ref auto int f() => 3; // Deprecation
^
対策は2通りあります。1つ目はキーワード順を auto ref に直して従来の意味を維持すること、2つ目は auto ref が不要なら ref のみ、または ref をやめて値返しに切り替え、意図を明確にすることです。
// 対策案2の例(常に ref で返す場合)
int value;
ref int h() { return value; }
元々 auto ref はテンプレートで処理しているどちらか特定できないことも多いので、基本は auto refの順序を守るのが正攻法です。
int op= float の暗黙整数変換が非推奨になりました
整数に対する代入、op=(例: +=)で浮動小数を整数へ暗黙変換する機能がありました。便利ではありますが、切り捨てが危険なため非推奨になりました。
変更前は uint += float のような式が暗黙に切り捨てて処理されていましたが、今後は非推奨の警告が出るようになります。意図しない丸めが起きやすい箇所なので、明示的に扱うことが求められます。
uint a;
float b = 0.1;
a += b; // Deprecation
// 修正例
a += cast(uint) b;
deprec-truncating-conversion/message.d(7): Deprecation: `uint += float` is performing truncating conversion
a += b; // Deprecation
^
対策は2通りあります。1つ目は明示的に変換して「切り捨てを意図している」ことを示す方法で、2つ目は変数側を浮動小数にして切り捨てを避ける方法です。必要なら std.conv.rountTo などを使ってみてください。
// 対策案1の例(明示的に切り捨てる)
uint a;
float b = 0.1f;
a += cast(uint) b;
// 対策案2の例(切り捨てを避ける)
float a;
float b = 0.1f;
a += b;
廃止/エラー
ぶら下がった else はエラーになりました
「ぶら下がった else」は、ネストした if で else の結び付きが曖昧になる状態(どの if に属するか読み手が判断しづらい状態)を指します。
以前は -wi 指定時のみ警告でしたが、今回からエラーになりました。
サンプルを見ると分かりますが、曖昧さを残したままコンパイルが通るのを防ぐ意図があります。
int i, j;
if (i)
if (j)
return 1;
else // Error: else is dangling
return 2;
error-dangling-else/message.d(8): Error: else is dangling, add { } after condition at error-dangling-else/message.d(5)
else // Error: else is dangling
^
対策は簡単で、ブロック構文を追加して else の属先を明示すればOKです。
常にブロックを明示する規約もあるくらいなので、これを機に見直しても良いかもしれません。
// 内側 if の else にする
if (i) {
if (j)
return 1;
else
return 2;
}
// 外側 if の else にする
if (i) {
if (j)
return 1;
} else {
return 2;
}
まとめ
dmd 2.112.0 は、「コンパイラ/ランタイムが計画通り着実に進歩していること」を強く感じるリリースでした。
コンパイラやランタイムのトピックとしては、ランタイムフックのテンプレート化、ビットフィールドの標準化、C++23 指定の受理、ImportC の __module、Windows DLL 向け -extI など、実務でお世話になりそうな更新が多めです。
また、ライブラリでも writeText 系・UUID v7・Unicode 更新・エントロピー周りの強化、--dest や frameworks など、日々の開発体験を底上げする追加が入っています。
2026年は、今回の基礎強化を踏まえて更なる最適化や安全性の強化、C/C++連携やImportC/BetterCなどの進歩が期待できそうです。
まずはぜひとも手元やCIで 2.112.0 を試し、気づきがあれば共有してください。
今年もD言語で楽しくプログラミングしていきましょう!