From c59cc95679ea04810df1c677c3bd44f5a8ceddaf Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Thu, 17 Sep 2026 23:13:30 +0700 Subject: [PATCH] The removal joins the deferred map operations in the test and in the note The arm was written with the others and through the same deferral, and neither the generics row in test_flan.ml nor the paragraph in BUILT.md that enumerates what defers had it. Its placeholder is get's, for get's reason: it answers an (Option V), so the match around it still has to check while the key is a variable. --- docs/BUILT.md | 7 ++++--- test/test_flan.ml | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/BUILT.md b/docs/BUILT.md index 866bf4b..5f33f1a 100644 --- a/docs/BUILT.md +++ b/docs/BUILT.md @@ -5393,9 +5393,10 @@ to name and nothing to choose between, so the abstract pass could not build the pair would have been worse than refusing: it would hash a string's pointer and a struct's padding. **What closes it is deferral, and what makes deferral safe is the `where` clause.** `put`, `get`, `has-key?`, -`reserve` and `clone` — the five arms that reach `key_fns` — now check their arguments and then, when the key is a -type variable, return a placeholder of the operation's own type: `Unit` for `put` and `reserve`, `None` for `get` -so the `(Option V)` around it still checks, `false` for `has-key?`, a zeroed map for `clone`. The whole node is +`map-remove!`, `reserve` and `clone` — the arms that reach `key_fns` — now check their arguments and then, when the +key is a type variable, return a placeholder of the operation's own type: `Unit` for `put` and `reserve`, `None` for +`get` and for `map-remove!` so the `(Option V)` around either still checks, `false` for `has-key?`, a zeroed map for +`clone`. The whole node is thrown away with the rest of the abstract pass, exactly as `println`'s is, and the real one is built when the copy is checked with `$t` concrete. diff --git a/test/test_flan.ml b/test/test_flan.ml index 73c4091..e8215a8 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -2676,6 +2676,12 @@ let () = accepts "get over a type-variable key answers an (Option V)" "(defn f [m (Map $t i32) k $t] i32 {:where (hashable? $t)} \ (match (get m k) (Some v) v _ 0))"; + (* And so does the removal, whose placeholder is [get]'s for the same reason: + it answers an (Option V), so the match around it still has to check while + the key is a variable. *) + accepts "map-remove! over a type-variable key answers an (Option V)" + "(defn f [m (Map $t i32) k $t] i32 {:where (hashable? $t)} \ + (match (map-remove! m k) (Some v) v _ 0))"; accepts "and so do has-key?, reserve and clone" "(defn f [m (Map $t i32) k $t] bool {:where (hashable? $t)} \ (do (reserve m 8) (let [c (clone m)] (free c) (has-key? m k))))";