From a0fea337b586202e8109c4a10ac8497f3569c03d Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:09:54 -0700 Subject: [PATCH 1/4] Port TS PR 61505 Cache mapper instantiations --- internal/checker/checker.go | 41 +++++++++++++++++++++++++++++++++++ internal/checker/inference.go | 1 + 2 files changed, 42 insertions(+) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index ca4dd45f4a..90a52abf22 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -856,6 +856,8 @@ type Checker struct { skipDirectInferenceNodes collections.Set[*ast.Node] ctx context.Context packagesMap map[string]bool + activeMappers []*TypeMapper + activeTypeMappersCaches []map[string]*Type } func NewChecker(program Program) *Checker { @@ -21174,14 +21176,53 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) return c.errorType } + index := c.findActiveMapper(m) + if index == -1 { + c.pushActiveMapper(m) + } + var b KeyBuilder + b.WriteType(t) + b.WriteAlias(alias) + key := b.String() + cache := c.activeTypeMappersCaches[core.IfElse(index != -1, index, len(c.activeTypeMappersCaches)-1)] + if cachedType, ok := cache[key]; ok { + return cachedType + } c.TotalInstantiationCount++ c.instantiationCount++ c.instantiationDepth++ result := c.instantiateTypeWorker(t, m, alias) + if index == -1 { + c.popActiveMapper() + } else { + cache[key] = result + } c.instantiationDepth-- return result } +func (c *Checker) pushActiveMapper(mapper *TypeMapper) { + c.activeMappers = append(c.activeMappers, mapper) + c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[string]*Type)) +} + +func (c *Checker) popActiveMapper() { + c.activeMappers[len(c.activeMappers)-1] = nil + c.activeTypeMappersCaches[len(c.activeTypeMappersCaches)-1] = nil + c.activeMappers = c.activeMappers[:len(c.activeMappers)-1] + c.activeTypeMappersCaches = c.activeTypeMappersCaches[:len(c.activeTypeMappersCaches)-1] +} + +func (c *Checker) findActiveMapper(mapper *TypeMapper) int { + return core.FindLastIndex(c.activeMappers, func(m *TypeMapper) bool { return m == mapper }) +} + +func (c *Checker) clearActiveMapperCaches() { + for _, cache := range c.activeTypeMappersCaches { + clear(cache) + } +} + // Return true if the given type could possibly reference a type parameter for which // we perform type inference (i.e. a type parameter of a generic function). We cache // results for union and intersection types for performance reasons. diff --git a/internal/checker/inference.go b/internal/checker/inference.go index 9ddbffd151..2d8db851cc 100644 --- a/internal/checker/inference.go +++ b/internal/checker/inference.go @@ -1329,6 +1329,7 @@ func (c *Checker) getInferredType(n *InferenceContext, index int) *Type { } inference.inferredType = inferredType } + c.clearActiveMapperCaches() } return inference.inferredType } From 77079b3ba3b0d910fd0d9141fa4e1672fcd4c938 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:23:29 -0700 Subject: [PATCH 2/4] Pool mapper caches --- internal/checker/checker.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 90a52abf22..d1a31860b6 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -21201,15 +21201,26 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl return result } +var activeMapperCache = sync.Pool{ + New: func() any { + return make(map[string]*Type, 1) + }, +} + func (c *Checker) pushActiveMapper(mapper *TypeMapper) { c.activeMappers = append(c.activeMappers, mapper) - c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[string]*Type)) + + c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, activeMapperCache.Get().(map[string]*Type)) } func (c *Checker) popActiveMapper() { c.activeMappers[len(c.activeMappers)-1] = nil - c.activeTypeMappersCaches[len(c.activeTypeMappersCaches)-1] = nil c.activeMappers = c.activeMappers[:len(c.activeMappers)-1] + + cache := c.activeTypeMappersCaches[len(c.activeTypeMappersCaches)-1] + c.activeTypeMappersCaches[len(c.activeTypeMappersCaches)-1] = nil + clear(cache) + activeMapperCache.Put(cache) c.activeTypeMappersCaches = c.activeTypeMappersCaches[:len(c.activeTypeMappersCaches)-1] } From a8f66bd8813337f47cab0cb4138b553d6eca8cdc Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:01:39 -0700 Subject: [PATCH 3/4] more similar change to TS --- internal/checker/checker.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index d1a31860b6..39e75d540a 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -21201,27 +21201,28 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl return result } -var activeMapperCache = sync.Pool{ - New: func() any { - return make(map[string]*Type, 1) - }, -} - func (c *Checker) pushActiveMapper(mapper *TypeMapper) { c.activeMappers = append(c.activeMappers, mapper) - c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, activeMapperCache.Get().(map[string]*Type)) + lastIndex := len(c.activeTypeMappersCaches) + if cap(c.activeTypeMappersCaches) > lastIndex { + c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex+1] + if c.activeTypeMappersCaches[lastIndex] == nil { + c.activeTypeMappersCaches[lastIndex] = make(map[string]*Type, 1) + } + } else { + c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[string]*Type, 1)) + } } func (c *Checker) popActiveMapper() { c.activeMappers[len(c.activeMappers)-1] = nil c.activeMappers = c.activeMappers[:len(c.activeMappers)-1] - cache := c.activeTypeMappersCaches[len(c.activeTypeMappersCaches)-1] - c.activeTypeMappersCaches[len(c.activeTypeMappersCaches)-1] = nil - clear(cache) - activeMapperCache.Put(cache) - c.activeTypeMappersCaches = c.activeTypeMappersCaches[:len(c.activeTypeMappersCaches)-1] + // Clear the map, but leave it in the list for later reuse. + lastIndex := len(c.activeTypeMappersCaches) - 1 + clear(c.activeTypeMappersCaches[lastIndex]) + c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex] } func (c *Checker) findActiveMapper(mapper *TypeMapper) int { From 0c317f24b1e327f66e9833034dc988d1845a1688 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:24:56 -0700 Subject: [PATCH 4/4] Commet --- internal/checker/checker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 39e75d540a..ec790d83c6 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -21206,6 +21206,7 @@ func (c *Checker) pushActiveMapper(mapper *TypeMapper) { lastIndex := len(c.activeTypeMappersCaches) if cap(c.activeTypeMappersCaches) > lastIndex { + // The cap may contain an empty map from popActiveMapper; reuse it. c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex+1] if c.activeTypeMappersCaches[lastIndex] == nil { c.activeTypeMappersCaches[lastIndex] = make(map[string]*Type, 1)