はじめに
D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.095.0
が 2021/01/03 にリリースされました。
いくつか非推奨機能が増えたこととDUBの強化が比較的大きいですが、便利機能追加および改善もあるので内容触れていきたいと思います。
より細かい内容は下記リンクからChangeLogをご覧ください。
- ChangeLog
また、前回11月のリリースまとめは以下になります。
- D言語の更新まとめ 2020年9月版(dmd 2.094.0)
変更点目次
コンパイラ変更点
- オーバーロードの解決でシンボルの可視性がバイパスされる動作が非推奨になりました
- テンプレートの内部で非推奨機能が使われた場合、インスタンス化のトレースが表示されるようになりました
- C++のヘッダー生成機能が改善されました
- モジュール内で競合する関数の定義が診断されるようになりました
-
extern(Pascal)
が削除されました - コンパイラに
-extern-std=c++20
のスイッチが追加されました - C++の互換性標準がデフォルトで C++11 になりました
-
-preview=inclusiveincontracts
:in
契約が親の契約の明示的なスーパーセットに限ることとするフラグを追加 - Objective-C のプロトコルサポートが追加されました
-
pragma(inline)
が任意のコンパイル時引数を許可するようになりました - C++シンボルの名前空間を取得する
__traits(getCppNamespaces, symbol)
が追加されました
改善点
- Bugzilla 8044:
enum
テンプレートパラメータを使用している場合、キャストされた値ではなく名前を表示するようになります - Bugzilla 21204: 生成されたコピーコンストラクタでエラーが発生した場合のメッセージを改善されます
- Bugzilla 21259: 非推奨のフィールドを持つ構造体の初期化は、非推奨の警告を発行するようになります
- Bugzilla 21275: オーバーロード解決が
private
アクセスを迂回できなくなります(変更点にある非推奨の件と同じ) - Bugzilla 21340:
extern(C++,(emptyTuple))
は名前空間が空の場合にエラーではなくなります
ランタイム変更点
-
suspendSignalNumber
とresumeSignalNumber
変数がprivate
となります
改善点
- Bugzilla 21030: 配列の平等性評価に関連したテンプレート関数のインスタンス化が削減されます
- Bugzilla 21417:
core.stdcpp.new_.cpp_delete
が@nogc
でも動作するようになります - Bugzilla 21426: 配列の値と連想配列のキーと値における
dup
とidup
:TypeInfo
の関数ポインタを経由せず、直接 postblit を呼び出すようになります
ライブラリ変更点
-
std.conv.parse
が消費した文字数を返せるようになります -
std.stdio.getdelim
とstd.stdio.getline
が非推奨になります -
JSONValue.get
で整数変換が追加されます
改善点
- Bugzilla 6484:
compose
が複数の引数を持つ関数を受け取れるようになります - Bugzilla 20869:
std.algorithm.mutation
:move
がopPostMove
の安全性を適切に評価するようになります - Bugzilla 20980:
std.bigint.BigInt
: 非負数に対して不要なアロケーションを行わなくなります - Bugzilla 21233:
std.conv.parse
が消費した文字数を報告できるようになります(変更点にある強化と同じ) - Bugzilla 21237:
isLvalueAssignable
とisRvalueAssignable
がpublic
になります - Bugzilla 21347:
std.functional.adjoin
が BetterC モードで動作するようになります - Bugzilla 21407:
std.math.NaN
とstd.math.getNaNPayload
が CTFE で動作するようになります - Bugzilla 21408: 拡張精度の
real
型に対しstd.math.nextUp
とnextDown
とnextafter
が CTFE で動作するようになります - Bugzilla 21430:
std.bitmanip.bitsSet
で返されるレンジオブジェクトのfront
,save
,length
プロパティにconst
が追加されます
DUB 変更点
- すべてのコマンドがバージョン指定を受け付けるようになります
- Dub が他の構成から mainSourceFile を自動的に除外するようになります
- visualdのプロジェクト生成で
-betterC
コンパイラフラグのサポートが追加されます -
dub test
で生成された単体テストランナーがキャッシュされるようになります - 依存関係に対してカスタムビルド設定を定義できるようになります
-
DFLAGS
とLFLAGS
がネストしたdub
の呼び出しに伝搬しないようになります - DC環境変数をデフォルトのDコンパイラとして使用するようになります
- DUB のシングルファイル形式でもユニットテストの実行が可能になります(#2051 の修正)
- LDCのクロスコンパイルを改善
-
list
コマンドにフィルタ機能が追加されます -
.netrc
ファイルのサポートが追加されます
注目トピック
言語・コンパイラ変更点
改善: pragma(inline)
が任意のコンパイル時引数を許可するようになりました
こちら元々 pragma(inline, true)
とすることで関数のインライン化を指定するものです。
この true
の部分にコンパイル時計算が可能な任意の式を利用できるようになるという改善です。
たとえば、以下の例はすべてインライン化するものとして扱われます。
pragma(inline, canInline("func1"))
void func1() {}
void func2()
{
pragma(inline, canInline(__traits(identifier, __traits(parent, {}))));
}
pragma(inline, canInline("func3") || true)
void func3() {}
int canInline(string fname)
{
switch (fname)
{
case "func1":
case "func2":
return 1;
default:
return 0;
}
}
void main()
{
func1();
func2();
func3();
}
利用方法として、設定でインライン化を切り替えてパフォーマンス調査する等が考えられそうです。
強化としてはなかなかニッチですが、実行速度も売りのひとつであるためこういった強化も重要な要素として扱われているのかと思います。
実験的機能: -preview=inclusiveincontracts
の追加
長いフラグ名を書き下すと inclusive in contracts
ということで、呼び出し側に課す制約である in
契約の動作を切り替えるフラグです。
今回このフラグが設けられた背景としては、契約プログラミングの前提となる「リスコフの置換原則」を厳守させたいという理由からです。
リスコフの置換原則をざっくり言うと、クラスの継承関係において、子クラスは親クラスとして振舞える必要がある、という原則になります。
特に事前条件について言えば、「親クラスが課す事前条件より厳しい事前条件を子クラスで課すことはできない」ということを表します。(置き換えが効かなくなるためロジックとして正しくない)
この条件が常に満たされるはずだという仮定から、D言語の実装としては内部的に「親クラスの条件 || 子クラスの条件
」として評価していました。
しかし実装上の組み合わせとして、親の条件より子の条件が厳しくなってしまうと上記の条件が「true || false
」と評価される場合も考えられます。
これはリスコフの置換原則を満たさないうえ、子クラスが要求する条件を満たさないにも関わらず、事前条件としてOKということです。どう考えても好ましくありません。
これを検出したいとの理由で、「true || false
」として評価された場合にロジック上のエラーとして扱うようにする、というのが -preview=inclusiveincontracts
の動作です。
安全性の観点からも、新規に何か作ることがあれば有効にしておくと良いと思います。
Changelogの抜粋ですが、以下のようなコードで実行時エラーが起きるようになります。
class A
{
void foo(int i) in (i > 0) { }
}
class B : A
{
void foo(int i) in (i < 0) { }
}
unittest
{
(new B).foo(5);
}
core.exception.AssertError@source\app.d(20): Logic error: in-contract was tighter than parent in-contract
ライブラリ変更点
強化: std.conv.parse
が消費した文字数を返せるようになります
文字列から数値に変換するために使う std.conv.parse
が強化されました。
これに関してはコードを見ていただくのが早いと思います。
import std.typecons : Flag, Yes, No;
string s1 = "123";
auto a1 = parse!(int, string, Yes.doCount)(s1);
assert(a1.data == 123 && a1.count == 3);
通常、parse
は数値に変換した結果だけを返しますが、先頭から数字を取っていって結果「何文字消費して数値を得たのか」という情報を取ることができます。
パーサーなどを書く場合、残りの部分をうまく使うことができるので処理の最適化につながります。
DUB変更点
強化: すべてのコマンドがバージョン指定を受け付けるようになります
dub fetch
など、パッケージ関連のコマンドでバージョン解決記法がサポートされます。
バージョン解決の記法というのは、dub.json
や dub.sdl
でパッケージのバージョンを指定するときに使う範囲記法です。
たとえば以下のようになります。
dub fetch "foo@>0.2.0"
強化: 依存関係に対してカスタムビルド設定を定義できるようになります
今回の強化の中ではなかなか重要な変更です。
依存している外部ライブラリのビルドに際し、細かいフラグ調整ができるようになります。
以下のような記述になります。
{
"name": "example",
"dependencies": {
"vibe-d": { "version" : "~>0.9.2", "dflags" : ["-preview=in"] }
}
}
もう1つ同じような変更で、以下の内容がありました。こちらの変更を実現するための副作用となります。
-
DFLAGS
とLFLAGS
がネストしたdub
の呼び出しに伝搬しないようになります
元々使う側で指定したフラグが依存先にも伝搬していたわけですが、これが個別に設定できることでフラグの設定しやすさが幾分改善すると想定されています。
このライブラリを使うときはこのフラグを指定してほしいといった要件があるとき、使う側としては指定したくないものでも使えるかもしれない、という強化です。
非推奨または廃止される機能
今回も新たに非推奨となる機能は2点、また以前から非推奨だった機能が1つ廃止されます。
非推奨
- オーバーロードの解決でシンボルの可視性がバイパスされる動作が非推奨になります
-
std.stdio.getdelim
とstd.stdio.getline
が非推奨になります
削除
-
extern(Pascal)
が削除されました
オーバーロード解決で private
なシンボルにアクセスされる場合がある
クラスなどの宣言において、プライベートメソッドとパブリックメソッドが同じオーバーロードセットに含まれている場合、プライベートなメソッドを別のモジュールから呼び出すことが可能な動作がありました。可視性にまつわるバグであり非推奨となります。
再現パターンがややこしいですが、公式サンプル抜粋すると以下のような場合です。
module foo;
struct Foo
{
import std : writeln;
private int _x;
private ref int x() return
{
writeln("ref int");
return _x;
}
int x() const
{
writeln("int");
return _x;
}
}
module main;
void main()
{
import std : writeln;
import foo : Foo;
auto f = Foo();
f.x = 3; // privateメソッドが呼び出される
writeln(f);
}
モジュール foo
に定義された構造体 Foo
のメンバーで、同じ x
という名前のオーバーロードがあります。
この x
は const
かどうかでオーバーロードされており、片方だけが private
という状況です。(そんなことする…?)
これに違反した場合、以下の非推奨を表す警告が表示されます。
main.d(9): Deprecation: Function foo.Foo.x of type ref int() return is not accessible from module main
こちら本来触れられないものになりますので、可視性の見直しか別の名前を付けることを検討しましょう。
std.stdio.getdelim
と std.stdio.getline
が非推奨になります
getdelim
と getline
は共にC言語のAPIになります。
元々 version (Posix)
の環境で core.sys.posix.stdio.getdelim
などが利用できるとき、core
以下のものがそのまま std.stdio
から参照できていた、というものです。
元の定義通り、 core.sys.posix.stdio
で同じ名前のシンボルが公開されますので、こちらの import
を利用すればOKです。
extern(Pascal)
が削除されました
extern(Pascal)
というのは関数呼び出し規則の1つです。
他にも extern(C)
や extern(C++)
などがありますが、これらは関数呼び出しに際してスタックやレジスタの使い方および後始末の方法を指定する意味があります。
今回削除された extern(Pascal)
は、文字通りプログラミング言語 Pascal で使われる関数呼び出し規則を指定します。
元々2019年1月の 2.084.0 で非推奨となりましたが、数十年前からほぼ利用されない遺産であること、DMDでしか実装されていないことによる混乱が削除の理由です。
多くの方は無関係かと思いますので気にしなくて大丈夫かと思われます。
(Delphiも同じ言語の系列なので同じ呼び出し規則かもしれませんが未確認です)
まとめ
機能的にはDUBの強化を中心にC++連携やObjective-Cなど他言語連携も強化改善が行われました。
非推奨の機能も出てきましたが、通常利用ではあまり使われないので、そこまで気にするものでもないかと思われます。
また昨年から新型コロナの影響でリリース時期が偶数月になるという話でしたが、気付いたら11月版をスキップすることで何もなかったように1月にリリースされました。
1回飛ばしているため実際の修正ボリュームはなかなか大きく、手元のプロジェクトは一度ビルドしてみると良いかと思います。
またリリーススケジュールは以下にありますが、今年は奇数月で継続とのことから引き続き強化に期待しつつ注視していきたいと思います。