Skip to content

C++26 : 「未初期化変数の読み取りをエラー性動作とする」を追加 #1349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
90c7ce3
C++26 : 「未初期化変数の読み取りを誤り起因動作とする」を追加
faithandbrave Sep 13, 2024
cd6d39b
C++26 未初期化変数の読み取り : erroneous behavior後の結果値に関する説明を整理
faithandbrave Sep 17, 2024
040c509
C++26 未初期化変数の読み取り : コード例にexampleをつけた
faithandbrave Sep 17, 2024
a114975
用語定義 : erroneous behaviorの概要を実態に合うよう修正
faithandbrave Sep 30, 2024
7f7759e
C++26 未初期化変数の読み取り : 概要を整理
faithandbrave Sep 30, 2024
6b0e3d7
C++26 未初期化変数の読み取り : assert結果を明記
faithandbrave Sep 30, 2024
2a4fe0e
C++26 未初期化変数の読み取り : 動的確保されたオブジェクトが対象外であることを記載
faithandbrave Sep 30, 2024
30cfb4e
erroneous behavior/valueを一旦訳さない
faithandbrave Oct 7, 2024
7d2188d
C++26 未初期化値の読み取り : 未定義動作になりえるケースを記載
faithandbrave Oct 7, 2024
c965e26
用語 : 読みを修正
faithandbrave Oct 8, 2024
7e68271
C++26 未初期化変数の読み取り : 訳してない用語のマーキング
faithandbrave Oct 8, 2024
aeee953
C++26 未初期化変数の読み取り : 仕様説明を見直し
faithandbrave Oct 8, 2024
5e8c37d
C++26 未初期化変数の読み取り : indeterminateのサンプルコードミスを修正
faithandbrave Oct 8, 2024
afee388
C++26 未初期化変数の読み取り : 抜けていた「常に真」を追加
faithandbrave Oct 8, 2024
94e04bf
C++26 未初期化変数の読み取り : boolの例示を見直し
faithandbrave Oct 8, 2024
a81c832
C++26 未初期化変数の読み取り : 「明確に定義された動作」を削除
faithandbrave Oct 10, 2024
616c00b
C++26 未初期化変数の読み取り : 今後の話を仕様から備考に移動
faithandbrave Oct 10, 2024
82d6360
C++26 未初期化変数の読み取り : 決定した訳語を適用
faithandbrave Nov 7, 2024
6010481
GLOBAL_DEFINED_WORDS: EB に倣い UB も英名を表記
akinomyoga Nov 7, 2024
28eb999
標準規格と処理系: エラー性の動作を言及
akinomyoga Nov 7, 2024
1c11eb8
fix typo
faithandbrave Nov 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion GLOBAL_DEFINED_WORDS.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"未定義の動作": {
"link": "/implementation-compliance.md#dfn-undefined-behavior",
"yomi": "みていぎのどうさ",
"desc": "処理系は予期せぬ動作をする可能性がある。要するに動作保証対象外"
"desc": "処理系は予期せぬ動作をする可能性がある。要するに動作保証対象外。undefined behavior (UB)。"
},
"未定義動作": { "redirect": "未定義の動作", "yomi": "みていぎどうさ" },
"未定義": { "redirect": "未定義の動作", "yomi": "みていぎ" },
Expand All @@ -23,6 +23,12 @@
},
"未規定動作": { "redirect": "未規定の動作", "yomi": "みきていどうさ" },
"未規定": { "redirect": "未規定の動作", "yomi": "みきてい" },
"エラー性の動作": {
"link": "/implementation-compliance.md#dfn-erroneous-behavior",
"yomi": "えらーせいのどうさ",
"desc": "未定義動作ではないが、誤ったプログラムの結果とされる動作。erroneous behavior (EB)。処理系によって診断や異常終了を実行することが許可されるが、処理が続行する場合もある"
},
"エラー性動作": { "redirect": "エラー性の動作", "yomi": "えらーせいどうさ" },
"処理系定義の動作": {
"link": "/implementation-compliance.md#dfn-implementation-defined-behavior",
"yomi": "しょりけいていぎのどうさ",
Expand Down
1 change: 1 addition & 0 deletions implementation-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ C++ の処理系は、翻訳を担うコンパイラと実行を担うオペレ
- <dfn id="dfn-unspecified-behavior">未規定の動作</dfn> (unspecified behavior) とされた動作に対しては、処理系は考えられる動作の内の1つを行って良い。処理系は説明書にその動作を定義しなくて良い。
- <dfn id="dfn-undefined-behavior">未定義の動作</dfn> (undefined behavior; 通称 UB) は、処理系が実際に行う動作について標準規格が如何なる要件もおかないことを表す。
- **文化圏固有動作** (locale-specific behavior) に対しては、処理系は現地の国家・文化・言語の風習に依存した動作を行う。処理系はその動作を説明書に記述する必要がある。
- <dfn id="dfn-erroneous-behavior">エラー性の動作</dfn> (erroneous behavior; 通称 EB) は、定義された動作であるが、処理系は診断情報を出力することが推奨される。その後に未規定の時点で、処理系は実行を終了しても良い。

これらの用語は処理系が取りうる動作の範囲を示すものであって、例えば "未定義の動作" という名前の具体的な動作がある訳ではないことに注意する。

Expand Down
2 changes: 1 addition & 1 deletion implementation-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
| P0609R3: [構造化束縛への属性を許可](/lang/cpp26/attributes_for_structured_bindings.md) | `auto [a, b [[maybe_unused]], c] = f();`のように構造化束縛の要素に対して属性を付加できるようにする | 15 | 19 | | |
| P3034R1: [モジュール宣言でのモジュール名のマクロ展開を禁止する](/lang/cpp26/module_declarations_shouldnt_be_macros.md.nolink) | `export module MACRO_NAME;`を禁止 | | | | |
| P2809R3: [自明な無限ループは未定義動作ではないと規定](/lang/cpp26/trivial_infinite_loops_are_not_undefined_behavior.md.nolink) | 並行プログラムの進行保証などを考慮して無限ループを未定義動作ではないものとする | 14 | 19 | | |
| P2795R5: [未初期化変数の読み取りを不正動作 (erroneous behaviour: EB) とする](/lang/cpp26/erroneous_behaviour_for_uninitialized_reads.md.nolink) | 初期化されていない自動変数の読み取りの安全性を規定する | | | | |
| P2795R5: [未初期化変数の読み取りをエラー性動作とする](/lang/cpp26/erroneous_behavior_for_uninitialized_reads.md) | 初期化されていない自動変数の読み取りの安全性を規定する | | | | |
| P2573R2: [関数宣言を削除する理由を指定できるようにする](/lang/cpp26/delete_reason.md) | `f() = delete("reason");` | 15 | 19 | | |
| P2893R3: [可変引数テンプレートで`friend`宣言をできるようにする](/lang/cpp26/variadic_friends.md.nolink) | クラステンプレートの可変引数テンプレートでまとめて`friend`宣言できるようにする | 15 | | | |
| P2747R2: [`constexpr`配置`new`](/lang/cpp26/constexpr_placement_new.md.nolink) | 定数式の文脈での配置`new`を許可 | | | | |
Expand Down
2 changes: 1 addition & 1 deletion lang/cpp26.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ C++26とは、2026年中に改訂される予定の、C++バージョンの通
| [不完全型へのポインタに対する`delete`を不適格とする](/lang/cpp26/deleting_a_pointer_to_an_incomplete_type_should_be_ill-formed.md.nolink) | 未定義動作となる操作をコンパイルエラーとする |
| [返却された左辺値から暗黙変換された一時オブジェクトが参照に束縛されることを禁止する](/lang/cpp26/disallow_binding_a_returned_glvalue_to_a_temporary.md.nolink) | 寿命切れの変数によって引き起こされるバグを防止する |
| [要素数不明の配列を集成体初期化する規則を明確化](/lang/cpp26/clarifying_rules_for_brace_elision_in_aggregate_initialization.md.nolink) | 配列要素の集成体初期化で`{}`が省略された場合の矛盾していた規定を修正 |
| [未初期化変数の読み取りを不正動作 (erroneous behaviour: EB) とする](/lang/cpp26/erroneous_behaviour_for_uninitialized_reads.md.nolink) | 初期化されていない自動変数の読み取りの安全性を規定する |
| [未初期化変数の読み取りをエラー性動作とする](/lang/cpp26/erroneous_behavior_for_uninitialized_reads.md) | 初期化されていない自動変数の読み取りの安全性を規定する |


### 文字列
Expand Down
141 changes: 141 additions & 0 deletions lang/cpp26/erroneous_behavior_for_uninitialized_reads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# 未初期化変数の読み取りをエラー性動作とする [P2795R5]
* cpp26[meta cpp]

<!-- start lang caution -->

このページはC++26に採用される見込みの言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため[関連項目](#relative-page)を参照してください。

<!-- last lang caution -->

## 概要
C++23までは、未初期化変数 (デフォルト初期化された変数) の読み取りは未定義動作として扱われていた。C++26では、この未定義動作による安全上のリスクを低減するため、「エラー性動作 (erroneous behavior; 通称 EB、別表記としてエラー性の動作)」を新設してその多くに割り当てることとした。

```cpp example
// C++23
void f(int) {}

int main() {
int x; // デフォルト初期化。xは不定値 (indeterminate value) をもつ
f(x); // 左辺値から右辺値への変換が未定義動作を引き起こす
}
```

未初期化の値は、コンパイラやターゲット環境によって定義された固定値である。コンパイラにはこの誤りを診断することが許可され、推奨されているが、誤りを無視して有効な読み取りとして扱うことも許可されている。このコードは誤りではあるが、実行ごとに異なる動作をしたり攻撃者に値を制御されたりといったリスクはなくなる。

エラー性動作は未定義動作(一切の保証が失われる)とはちがって、動作を定めたうえで診断も可能とするものである。

C++26では、不定値で初期化されることを明確に指示する`[[indeterminate]]`属性も導入され、以下のような動作となる:

```cpp example
// C++26
void f(int) {}

int main() {
int x; // xはエラー性の値をもつ
int y [[indeterminate]]; // 意図して不定値に初期化されることを指示

f(x); // エラー性動作 (エラー性の値の読み取り)
f(y); // 未定義動作 (不定値の読み取り)
}
```


## 仕様
- 自動記憶域期間をもつオブジェクトの記憶域は確保時点で「エラー性の値 (erroneous value)」をもつとされ、処理系がプログラムの状態に依存せず決定する何らかの値で埋められる
- 動的記憶域期間であれば不定値、静的・スレッド記憶域期間であればゼロで埋められる。C++23までは自動記憶域期間も不定値で埋められていた
- 初期化されなかったスカラ型オブジェクトなど、値表現(パディングは含まない)内のいずれかのビットにエラー性の値をもつオブジェクトはエラー性の値をもつとされる
- 式が評価された結果としてエラー性の値が生成された場合、エラー性動作を引き起こす
- ただし、`unsigned char`(およびunsignedとなる場合は`char`)もしくは[`std::byte`](/reference/cstddef/byte.md)型のエラー性の値がこれらの型のオブジェクトの初期化・代入に使用される場合や値が破棄される場合はエラー性動作にならない
- これらのルールは、式が評価された結果として不定値が生成された場合に未定義の動作を引き起こすとする従来のルールと同様である
- エラー性動作を引き起こしたうえで生成された値は、後続の処理ではエラー性の値とはみなされない

```cpp example
#include <cassert>

int g(bool b) {
unsigned char c;
unsigned char d = c; // エラー性動作ではない。dはエラー性の値をもつ

assert(c == d); // 常に真、エラー性動作 (整数昇格)

int e = d; // エラー性動作 (型変換)
return b ? d : 0; // bがtrueの場合にエラー性動作
}

int main() {
int d1, d2;

int e1 = d1; // エラー性動作
int e2 = d1; // エラー性動作

// 処理が続行した場合…
assert(e1 == e2); // 常に真、エラー性動作ではない。
// エラー性動作の結果で生成された値 (e1とe2) は、
// エラー性の値とはみなされない
assert(e1 == d1); // 常に真、エラー性動作
assert(e2 == d1); // 常に真、エラー性動作

// エラー性動作ではないが
// d2はエラー性の値をもつ
std::memcpy(&d2, &d1, sizeof(int));

assert(e1 == d2); // 常に真、エラー性動作
assert(e2 == d2); // 常に真、エラー性動作
}
```

### `[[indeterminate]]`属性
`[[indeterminate]]`属性は、自動変数が初期状態として意図して不定値をもつことを指示するものであり、自動変数の定義、もしくは関数のパラメータ宣言に適用できる。

関数のパラメータが`[[indeterminate]]`属性で宣言される場合、その関数の最初の宣言でそのように宣言されなければならない (注:関数宣言は複数行うことができるが、その最初の宣言で`[[indeterminate]]`属性をつけなければならない)。

`[[indeterminate]]`がつけられた変数から読み取りをした場合、未定義動作を引き起こす可能性がある。

```cpp example
struct T {
T() {}
int x;
};

int h(T t [[indeterminate]]) {
f(t.x); // 後述の関数呼び出しはここで未定義動作を引き起こす
return 0;
}

int main() {
int _ = h(T());
}
```

## 備考
- 以下のようなケースでは、エラー性動作ではなく未定義動作を引き起こす可能性がある:
```cpp
T* p; // 未初期化のポインタ。エラー性の値 (例としてヌルポインタ) をもつ
bool b; // 未初期化の真理値。
// bool値として妥当な値表現をもたない可能性がある
// (例: 値表現が8bitで{0x00(false), 0x01(true)}の
// 2通りのみとする処理系で0xCCをもつなど)

f(*p); // 間接参照は未定義動作を引き起こす
g(b); // bが妥当な値表現をもつ場合にエラー性動作、そうでなければ未定義動作
```

### 今後、エラー性動作に分類される可能性のある操作

現在、未定義動作に分類される以下の操作は、エラー性動作に分類できる可能性がある。

| 操作 | 備考 |
|------|------|
| 符号付き整数のオーバーフロー | 演算結果としてオーバーフローした場合に誤った結果になる可能性がある。これは珍しいバグではない。これは安全上の大きな問題ではない |
| 算術型の変換結果としてその型の表現可能な範囲を超えた | 符号付き整数のオーバーフローと同じ |
| 誤ったビットシフト (負のシフト幅や、上限を超えたシフト幅) | 符号付き整数のオーバーフローと同じ |
| ゼロ割り | いくつかの固定値での誤った結果となる可能性がある。影響が不明確であるため、変更にはコストがかかる |
| 戻り値型が非`void`な関数から返った、もしくは`[[noreturn]]`属性をつけた関数から返った | [`std::terminate()`](/reference/exception/terminate.md)が呼ばれる可能性がある。変更には軽いコストがかかるが、その変更にどの程度の価値があるかは不明 |
| 抽象クラスのコンストラクタ・デストラクタからの純粋仮想関数の呼び出し | 特定の純粋仮想ハンドラが呼ばれる可能性がある。実装によってはすでにエラー性動作のように扱われている可能性がある |
| 契約違反 | 契約に関する現在の策定作業では、契約違反時になにが起こるべきかという問題に直面している。エラー性動作という概念は有用な回答を与えてくれる可能性がある |


## 参照
- [P2795R5 Erroneous behaviour for uninitialized reads](https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2795r5.html)
Loading