-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor to make sigmatch use LayeredIdTable for bindings (#24216)
split from #24198 This is a required refactor for the only good solution I've been able to think of for #4858 etc. Explanation: --- `sigmatch` currently [disables bindings](https://github.com/nim-lang/Nim/blob/d6a71a10671b66ee4f5be09f99234b3d834e7fce/compiler/sigmatch.nim#L1956) (except for binding to other generic parameters) when matching against constraints of generic parameters. This is so when the constraint is a general metatype like `seq`, the type matching will not treat all following uses of `seq` as the type matched against that generic parameter. However to solve #4858 etc we need to bind `or` types with a conversion match to the type they are supposed to be converted to (i.e. matching `int literal(123)` against `int8 | int16` should bind `int8`[^1], not `int`). The generic parameter constraint binding needs some way to keep track of this so that matching `int literal(123)` against `T: int8 | int16` also binds `T` to `int8`[^1]. The only good way to do this IMO is to generate a new "binding context" when matching against constraints, then binding the generic param to what the constraint was bound to in that context (in #24198 this is restricted to just `or` types & concrete types with convertible matches, it doesn't work in general). --- `semtypinst` already does something similar for bindings of generic invocations using `LayeredIdTable`, so `LayeredIdTable` is now split into its own module and used in `sigmatch` for type bindings as well, rather than a single-layer `TypeMapping`. Other modules which act on `sigmatch`'s binding map are also updated to use this type instead. The type is also made into an `object` type rather than a `ref object` to reduce the pointer indirection when embedding it inside `TCandidate`/`TReplTypeVars`, but only on arc/orc since there are some weird aliasing bugs on refc/markAndSweep that cause a segfault when setting a layer to its previous layer. If we want we can also just remove the conditional compilation altogether and always use `ref object` at the cost of some performance. [^1]: `int8` binding here and not `int16` might seem weird, since they match equally well. But we need to resolve the ambiguity here, in #24012 I tested disallowing ambiguities like this and it broke many packages that tries to match int literals to things like `int16 | uint16` or `int8 | int16`. Instead of making these packages stop working I think it's better we resolve the ambiguity with a rule like "the earliest `or` branch with the best match, matches". This is the rule used in #24198.
- Loading branch information
Showing
10 changed files
with
149 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import std/tables | ||
import ast | ||
|
||
type | ||
LayeredIdTableObj* {.acyclic.} = object | ||
## stack of type binding contexts implemented as a linked list | ||
topLayer*: TypeMapping | ||
## the mappings on the current layer | ||
nextLayer*: ref LayeredIdTableObj | ||
## the parent type binding context, possibly `nil` | ||
previousLen*: int | ||
## total length of the bindings up to the parent layer, | ||
## used to track if new bindings were added | ||
|
||
const useRef = not defined(gcDestructors) | ||
# implementation detail, only arc/orc doesn't cause issues when | ||
# using LayeredIdTable as an object and not a ref | ||
|
||
when useRef: | ||
type LayeredIdTable* = ref LayeredIdTableObj | ||
else: | ||
type LayeredIdTable* = LayeredIdTableObj | ||
|
||
proc initLayeredTypeMap*(pt: sink TypeMapping = initTypeMapping()): LayeredIdTable = | ||
result = LayeredIdTable(topLayer: pt, nextLayer: nil) | ||
|
||
proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} = | ||
## copies only the type bindings of the current layer, but not any parent layers, | ||
## useful for write-only bindings | ||
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen) | ||
|
||
proc currentLen*(pt: LayeredIdTable): int = | ||
## the sum of the cached total binding count of the parents and | ||
## the current binding count, just used to track if bindings were added | ||
pt.previousLen + pt.topLayer.len | ||
|
||
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable = | ||
result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen) | ||
when useRef: | ||
result.nextLayer = pt | ||
else: | ||
new(result.nextLayer) | ||
result.nextLayer[] = pt | ||
|
||
proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} = | ||
when useRef: | ||
pt = pt.nextLayer | ||
else: | ||
when defined(gcDestructors): | ||
pt = pt.nextLayer[] | ||
else: | ||
# workaround refc | ||
let tmp = pt.nextLayer[] | ||
pt = tmp | ||
|
||
proc lookup(typeMap: ref LayeredIdTableObj, key: ItemId): PType = | ||
result = nil | ||
var tm = typeMap | ||
while tm != nil: | ||
result = getOrDefault(tm.topLayer, key) | ||
if result != nil: return | ||
tm = tm.nextLayer | ||
|
||
template lookup*(typeMap: ref LayeredIdTableObj, key: PType): PType = | ||
## recursively looks up binding of `key` in all parent layers | ||
lookup(typeMap, key.itemId) | ||
|
||
when not useRef: | ||
proc lookup(typeMap: LayeredIdTableObj, key: ItemId): PType {.inline.} = | ||
result = getOrDefault(typeMap.topLayer, key) | ||
if result == nil and typeMap.nextLayer != nil: | ||
result = lookup(typeMap.nextLayer, key) | ||
|
||
template lookup*(typeMap: LayeredIdTableObj, key: PType): PType = | ||
lookup(typeMap, key.itemId) | ||
|
||
proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} = | ||
typeMap.topLayer[key] = value | ||
|
||
template put*(typeMap: var LayeredIdTable, key, value: PType) = | ||
## binds `key` to `value` only in current layer | ||
put(typeMap, key.itemId, value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.