Record spread that copies a mutable field is generalized (unsound)

クローズ
#9,156 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

関連するプルリクエストがすでにマージされています。

  • #9158 @cknitt による — マージ済み

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
38/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
ocaml
領域
compilers

調査の方向性

The unsound generalization comes from the value restriction in Typecore.is_nonexpansive, specifically how it handles record literals with a spread: written mutable fields are checked, but copied ones are not. Start there and compare with the handling of a plain record literal, then reproduce with the cell<'a> snippet from the issue using an external base. Done means a spread that copies a mutable field is treated as expansive, the example no longer type-checks, and existing type tests still pass.

索引モデルが issue の本文から書いたものです。

説明

A record literal with a spread, {...base, field: value}, has its type generalized even when it copies a mutable field from base. The literal allocates a fresh record with that mutable field, so one binding can be written at one type and read at another:

type cell<'a> = {tag: int, mutable value: option<'a>}

@val external base: cell<'a> = "base"

let copy = {...base, tag: 1}
copy.value = Some(1)
let s: option<string> = copy.value

This compiles on master (8e26a9ca69) and generates:

let init = base;
let copy = {
  tag: 1,
  value: init.value
};
copy.value = 1;
let s = copy.value;

so s, typed option<string>, holds the number 1. The same happens with optional mutable fields ({tag?: int, mutable value?: 'a}).

Cause

The value restriction (Typecore.is_nonexpansive) checks a spread literal's written fields for mutability and requires the spread expression to be non-expansive, but treats the fields it copies from the spread as harmless. A spread literal allocates every field of the record, so a mutable field makes it expansive whether it is written or copied, just like a literal without a spread (#9149, #9152).

base has to be a polymorphic, non-expansive value of a record type with a mutable field. A record literal of such a type is itself expansive, so in practice base is an external, for example a binding to a default options object: @val external defaults: options<'a> followed by {...defaults, x: 1}.

主要言語
OCaml
スター
7.5k
フォーク
484
平均マージ
17時間 2分
マージ済み PR(30日)
249

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

rescript-lang/rescript のほかの issue

rescript-lang/rescript の issue をすべて見る

似ている issue

Compilers の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。