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

Fermée
#9,156 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

Une pull request liée a déjà été fusionnée.

  • #9158 par @cknitt — fusionnée

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
38/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
ocaml
Domaine
compilers

Piste de recherche

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.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

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}.

Langage dominant
OCaml
Étoiles
7.5k
Forks
484
Merge moyen
17 h 2 min
PR mergées (30 j)
249

Préparer son environnement

Ouvrir dans Codespaces

Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de rescript-lang/rescript

Toutes les issues de rescript-lang/rescript

Issues similaires

Plus d'issues Compilers

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.