From 2660cfa97ca76d60a1c3c03c495716ccbac07fab Mon Sep 17 00:00:00 2001 From: "ajeffrey@roblox.com" Date: Wed, 8 Sep 2021 14:22:25 -0500 Subject: [PATCH] Add first draft of rfcs/unsealed-table-assign-indexer.md --- rfcs/unsealed-table-assign-indexer.md | 61 +++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 rfcs/unsealed-table-assign-indexer.md diff --git a/rfcs/unsealed-table-assign-indexer.md b/rfcs/unsealed-table-assign-indexer.md new file mode 100644 index 00000000..632c152f --- /dev/null +++ b/rfcs/unsealed-table-assign-indexer.md @@ -0,0 +1,61 @@ +# Unsealed table assignment creates an indexer + +In Luau, tables have a state, which can, among others, be "unsealed". +An unsealed table is one that we are still constructing. Currently +assigning a table literal to an unsealed table creates new properties, +but this is a problem in cases where tables are being used as hash +tables. We would like to change this so that assigning a table +literal to an unsealed table creates an indexer rather than +properties. + +## Motivation + +In lua-apps, there is testing code which (simplified) looks like: + +```lua +local t = { u = {} } +t = { u = { p = 37 } } +t = { u = { q = 5 } } +local x: number? = t.u["p"] +local y: number? = t.u["q"] +``` + +Currently, this code doesn't typecheck, due to `p` and `q` being unknown properties of `t`. + +In order to support this idiom, we propose that assigning a table with +properties of type `T` to to an unsealed table with no properties +should introduce a string indexer of type `T`. + +For example, with this change the type of `t` is `{ u: { [string]: number } }`. + +## Drawbacks + +Currently table literals are sealed tables, so support width subtyping, which causes +the system to be unsound, for example: + +```lua +local t = { u = {} } +local u : { p = 37 } = { p = 37, q = "hi" } +t = { u = u } +local x: number? = t.u["p"] +local y: number? = t.u["q"] -- This is "hi" at run-time +``` + +The fix for this would be for table literals to be unsealed tables, and to only introduce an indexer when the subtype is unsealed. + +This fix needs a change to lua-apps, since sometimes the code uses +assignment with a table literal to add properties, and sometimes to +initialize a hash table. The code currently in lua-apps looks like: + +```lua +local t = { } -- Note: no u property +t = { u = { p = 37 } } +t = { u = { q = 5 } } +local x: number? = t.u["p"] +local y: number? = t.u["q"] +``` + +## Alternatives + +Rather than introducing an indexer, we could introduce optional properties. For example we could infer the type of +`t` as `{ u: { p: number?, q: number? } }`. \ No newline at end of file