diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 8f5065c9be..6dfeedc6e7 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -27,6 +27,7 @@ import ( "github.com/microsoft/typescript-go/internal/stringutil" "github.com/microsoft/typescript-go/internal/tsoptions" "github.com/microsoft/typescript-go/internal/tspath" + "github.com/zeebo/xxh3" ) // CheckMode @@ -161,23 +162,23 @@ type UnionOfUnionKey struct { id1 TypeId id2 TypeId r UnionReduction - a string + a xxh3.Uint128 } // CachedSignatureKey type CachedSignatureKey struct { sig *Signature - key string // Type list key or one of the strings below + key xxh3.Uint128 // Type list key or one of the special keys below } -const ( - SignatureKeyErased string = "-" - SignatureKeyCanonical string = "*" - SignatureKeyBase string = "#" - SignatureKeyInner string = "<" - SignatureKeyOuter string = ">" - SignatureKeyImplementation string = "+" +var ( + SignatureKeyErased = xxh3.HashString128("-") + SignatureKeyCanonical = xxh3.HashString128("*") + SignatureKeyBase = xxh3.HashString128("#") + SignatureKeyInner = xxh3.HashString128("<") + SignatureKeyOuter = xxh3.HashString128(">") + SignatureKeyImplementation = xxh3.HashString128("+") ) // StringMappingKey @@ -234,7 +235,7 @@ type IterationTypesKey struct { type FlowLoopKey struct { flowNode *ast.FlowNode - refKey string + refKey xxh3.Uint128 } type FlowLoopInfo struct { @@ -595,13 +596,13 @@ type Checker struct { numberLiteralTypes map[jsnum.Number]*Type bigintLiteralTypes map[jsnum.PseudoBigInt]*Type enumLiteralTypes map[EnumLiteralKey]*Type - indexedAccessTypes map[string]*Type - templateLiteralTypes map[string]*Type + indexedAccessTypes map[xxh3.Uint128]*Type + templateLiteralTypes map[xxh3.Uint128]*Type stringMappingTypes map[StringMappingKey]*Type uniqueESSymbolTypes map[*ast.Symbol]*Type thisExpandoKinds map[*ast.Symbol]thisAssignmentDeclarationKind thisExpandoLocations map[*ast.Symbol]*ast.Node - subtypeReductionCache map[string][]*Type + subtypeReductionCache map[xxh3.Uint128][]*Type cachedTypes map[CachedTypeKey]*Type cachedSignatures map[CachedSignatureKey]*Signature undefinedProperties map[string]*ast.Symbol @@ -619,14 +620,14 @@ type Checker struct { requireSymbol *ast.Symbol unknownSymbol *ast.Symbol unresolvedSymbols map[string]*ast.Symbol - errorTypes map[string]*Type + errorTypes map[xxh3.Uint128]*Type globalThisSymbol *ast.Symbol resolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol resolveNameForSymbolSuggestion func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol - tupleTypes map[string]*Type - unionTypes map[string]*Type + tupleTypes map[xxh3.Uint128]*Type + unionTypes map[xxh3.Uint128]*Type unionOfUnionTypes map[UnionOfUnionKey]*Type - intersectionTypes map[string]*Type + intersectionTypes map[xxh3.Uint128]*Type diagnostics ast.DiagnosticsCollection suggestionDiagnostics ast.DiagnosticsCollection symbolPool core.Pool[ast.Symbol] @@ -853,7 +854,7 @@ type Checker struct { ctx context.Context packagesMap map[string]bool activeMappers []*TypeMapper - activeTypeMappersCaches []map[string]*Type + activeTypeMappersCaches []map[xxh3.Uint128]*Type ambientModulesOnce sync.Once ambientModules []*ast.Symbol withinUnreachableCode bool @@ -896,13 +897,13 @@ func NewChecker(program Program) (*Checker, *sync.Mutex) { c.numberLiteralTypes = make(map[jsnum.Number]*Type) c.bigintLiteralTypes = make(map[jsnum.PseudoBigInt]*Type) c.enumLiteralTypes = make(map[EnumLiteralKey]*Type) - c.indexedAccessTypes = make(map[string]*Type) - c.templateLiteralTypes = make(map[string]*Type) + c.indexedAccessTypes = make(map[xxh3.Uint128]*Type) + c.templateLiteralTypes = make(map[xxh3.Uint128]*Type) c.stringMappingTypes = make(map[StringMappingKey]*Type) c.uniqueESSymbolTypes = make(map[*ast.Symbol]*Type) c.thisExpandoKinds = make(map[*ast.Symbol]thisAssignmentDeclarationKind) c.thisExpandoLocations = make(map[*ast.Symbol]*ast.Node) - c.subtypeReductionCache = make(map[string][]*Type) + c.subtypeReductionCache = make(map[xxh3.Uint128][]*Type) c.cachedTypes = make(map[CachedTypeKey]*Type) c.cachedSignatures = make(map[CachedSignatureKey]*Signature) c.undefinedProperties = make(map[string]*ast.Symbol) @@ -919,16 +920,16 @@ func NewChecker(program Program) (*Checker, *sync.Mutex) { c.requireSymbol = c.newSymbol(ast.SymbolFlagsProperty, "require") c.unknownSymbol = c.newSymbol(ast.SymbolFlagsProperty, "unknown") c.unresolvedSymbols = make(map[string]*ast.Symbol) - c.errorTypes = make(map[string]*Type) + c.errorTypes = make(map[xxh3.Uint128]*Type) c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) c.globalThisSymbol.Exports = c.globals c.globals[c.globalThisSymbol.Name] = c.globalThisSymbol c.resolveName = c.createNameResolver().Resolve c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve - c.tupleTypes = make(map[string]*Type) - c.unionTypes = make(map[string]*Type) + c.tupleTypes = make(map[xxh3.Uint128]*Type) + c.unionTypes = make(map[xxh3.Uint128]*Type) c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type) - c.intersectionTypes = make(map[string]*Type) + c.intersectionTypes = make(map[xxh3.Uint128]*Type) c.mergedSymbols = make(map[*ast.Symbol]*ast.Symbol) c.patternForType = make(map[*Type]*ast.Node) c.contextFreeTypes = make(map[*ast.Node]*Type) @@ -985,7 +986,7 @@ func NewChecker(program Program) (*Checker, *sync.Mutex) { c.unknownEmptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.unknownUnionType = c.createUnknownUnionType() c.emptyGenericType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.emptyGenericType.AsObjectType().instantiations = make(map[string]*Type) + c.emptyGenericType.AsObjectType().instantiations = make(map[xxh3.Uint128]*Type) c.anyFunctionType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.anyFunctionType.objectFlags |= ObjectFlagsNonInferrableType c.noConstraintType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) @@ -16829,7 +16830,7 @@ func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type { d.allTypeParameters = append(typeParameters, d.thisType) d.outerTypeParameterCount = len(outerTypeParameters) d.resolvedTypeArguments = d.TypeParameters() - d.instantiations = make(map[string]*Type) + d.instantiations = make(map[xxh3.Uint128]*Type) d.instantiations[getTypeListKey(d.resolvedTypeArguments)] = t d.target = t } @@ -16864,85 +16865,89 @@ func (c *Checker) isThislessInterface(symbol *ast.Symbol) bool { return true } -type KeyBuilder struct { - strings.Builder +func hashWrite32[T ~int32 | ~uint32](h *xxh3.Hasher, value T) { + v := uint32(value) + _, _ = h.Write([]byte{ + byte(v), + byte(v >> 8), + byte(v >> 16), + byte(v >> 24), + }) } -var base64chars = []byte{ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '$', '%', +func hashWrite64[T ~int | ~uint | ~int64 | ~uint64](h *xxh3.Hasher, value T) { + v := uint64(value) + _, _ = h.Write([]byte{ + byte(v), + byte(v >> 8), + byte(v >> 16), + byte(v >> 24), + byte(v >> 32), + byte(v >> 40), + byte(v >> 48), + byte(v >> 56), + }) } -func (b *KeyBuilder) WriteUint64(value uint64) { - for value != 0 { - b.WriteByte(base64chars[value&0x3F]) - value >>= 6 - } +type keyBuilder struct { + h xxh3.Hasher } -func (b *KeyBuilder) WriteInt(value int) { - b.WriteUint64(uint64(int64(value))) +func (b *keyBuilder) hash() xxh3.Uint128 { + return b.h.Sum128() } -func (b *KeyBuilder) WriteSymbolId(id ast.SymbolId) { - b.WriteUint64(uint64(id)) +func (b *keyBuilder) writeByte(c byte) { + _, _ = b.h.Write([]byte{c}) } -func (b *KeyBuilder) WriteSymbol(s *ast.Symbol) { - b.WriteSymbolId(ast.GetSymbolId(s)) +func (b *keyBuilder) writeString(s string) { + _, _ = b.h.WriteString(s) } -func (b *KeyBuilder) WriteTypeId(id TypeId) { - b.WriteUint64(uint64(id)) +func (b *keyBuilder) writeInt(value int) { + hashWrite64(&b.h, value) } -func (b *KeyBuilder) WriteType(t *Type) { - b.WriteTypeId(t.id) +func (b *keyBuilder) writeSymbolId(id ast.SymbolId) { + hashWrite64(&b.h, id) } -func (b *KeyBuilder) WriteTypes(types []*Type) { - i := 0 - var tail bool - for i < len(types) { - startId := types[i].id - count := 1 - for i+count < len(types) && types[i+count].id == startId+TypeId(count) { - count++ - } - if tail { - b.WriteByte(',') - } - b.WriteTypeId(startId) - if count > 1 { - b.WriteByte(':') - b.WriteInt(count) - } - i += count - tail = true +func (b *keyBuilder) writeSymbol(s *ast.Symbol) { + b.writeSymbolId(ast.GetSymbolId(s)) +} + +func (b *keyBuilder) writeTypeId(id TypeId) { + hashWrite32(&b.h, id) +} + +func (b *keyBuilder) writeType(t *Type) { + b.writeTypeId(t.id) +} + +func (b *keyBuilder) writeTypes(types []*Type) { + b.writeInt(len(types)) + for _, t := range types { + b.writeType(t) } } -func (b *KeyBuilder) WriteAlias(alias *TypeAlias) { +func (b *keyBuilder) writeAlias(alias *TypeAlias) { if alias != nil { - b.WriteByte('@') - b.WriteSymbol(alias.symbol) - if len(alias.typeArguments) != 0 { - b.WriteByte(':') - b.WriteTypes(alias.typeArguments) - } + b.writeByte(1) + b.writeSymbol(alias.symbol) + b.writeTypes(alias.typeArguments) + } else { + b.writeByte(0) } } -func (b *KeyBuilder) WriteGenericTypeReferences(source *Type, target *Type, ignoreConstraints bool) bool { +func (b *keyBuilder) writeGenericTypeReferences(source *Type, target *Type, ignoreConstraints bool) bool { var constrained bool typeParameters := make([]*Type, 0, 8) var writeTypeReference func(*Type, int) - // writeTypeReference(A) writes "111=0-12=1" - // where A.id=111 and number.id=12 writeTypeReference = func(ref *Type, depth int) { - b.WriteType(ref.Target()) + b.writeType(ref.Target()) for _, t := range ref.AsTypeReference().resolvedTypeArguments { if t.flags&TypeFlagsTypeParameter != 0 { if ignoreConstraints || t.checker.getConstraintOfTypeParameter(t) == nil { @@ -16951,193 +16956,186 @@ func (b *KeyBuilder) WriteGenericTypeReferences(source *Type, target *Type, igno index = len(typeParameters) typeParameters = append(typeParameters, t) } - b.WriteByte('=') - b.WriteInt(index) + b.writeByte('=') + b.writeInt(index) continue } constrained = true } else if depth < 4 && isTypeReferenceWithGenericArguments(t) { - b.WriteByte('<') + b.writeByte('<') writeTypeReference(t, depth+1) - b.WriteByte('>') + b.writeByte('>') continue } - b.WriteByte('-') - b.WriteType(t) + b.writeByte('-') + b.writeType(t) } } writeTypeReference(source, 0) - b.WriteByte(',') + b.writeByte(',') writeTypeReference(target, 0) return constrained } -func (b *KeyBuilder) WriteNodeId(id ast.NodeId) { - b.WriteUint64(uint64(id)) +func (b *keyBuilder) writeNodeId(id ast.NodeId) { + hashWrite64(&b.h, id) } -func (b *KeyBuilder) WriteNode(node *ast.Node) { +func (b *keyBuilder) writeNode(node *ast.Node) { if node != nil { - b.WriteNodeId(ast.GetNodeId(node)) + b.writeNodeId(ast.GetNodeId(node)) } } -func getTypeListKey(types []*Type) string { - var b KeyBuilder - b.WriteTypes(types) - return b.String() +func getTypeListKey(types []*Type) xxh3.Uint128 { + var b keyBuilder + b.writeTypes(types) + return b.hash() } -func getAliasKey(alias *TypeAlias) string { - var b KeyBuilder - b.WriteAlias(alias) - return b.String() +func getAliasKey(alias *TypeAlias) xxh3.Uint128 { + var b keyBuilder + b.writeAlias(alias) + return b.hash() } -func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) string { - var b KeyBuilder +func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) xxh3.Uint128 { + var b keyBuilder switch { case origin == nil: - b.WriteTypes(types) + b.writeTypes(types) case origin.flags&TypeFlagsUnion != 0: - b.WriteByte('|') - b.WriteTypes(origin.Types()) + b.writeByte('|') + b.writeTypes(origin.Types()) case origin.flags&TypeFlagsIntersection != 0: - b.WriteByte('&') - b.WriteTypes(origin.Types()) + b.writeByte('&') + b.writeTypes(origin.Types()) case origin.flags&TypeFlagsIndex != 0: // origin type id alone is insufficient, as `keyof x` may resolve to multiple WIP values while `x` is still resolving - b.WriteByte('#') - b.WriteType(origin) - b.WriteByte('|') - b.WriteTypes(types) + b.writeByte('#') + b.writeType(origin) + b.writeByte('|') + b.writeTypes(types) default: panic("Unhandled case in getUnionKey") } - b.WriteAlias(alias) - return b.String() + b.writeAlias(alias) + return b.hash() } -func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias) string { - var b KeyBuilder - b.WriteTypes(types) +func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias) xxh3.Uint128 { + var b keyBuilder + b.writeTypes(types) if flags&IntersectionFlagsNoConstraintReduction == 0 { - b.WriteAlias(alias) + b.writeAlias(alias) } else { - b.WriteByte('*') + b.writeByte('*') } - return b.String() + return b.hash() } -func getTupleKey(elementInfos []TupleElementInfo, readonly bool) string { - var b KeyBuilder +func getTupleKey(elementInfos []TupleElementInfo, readonly bool) xxh3.Uint128 { + var b keyBuilder for _, e := range elementInfos { switch { case e.flags&ElementFlagsRequired != 0: - b.WriteByte('#') + b.writeByte('#') case e.flags&ElementFlagsOptional != 0: - b.WriteByte('?') + b.writeByte('?') case e.flags&ElementFlagsRest != 0: - b.WriteByte('.') + b.writeByte('.') default: - b.WriteByte('*') + b.writeByte('*') } if e.labeledDeclaration != nil { - b.WriteNode(e.labeledDeclaration) + b.writeNode(e.labeledDeclaration) } } if readonly { - b.WriteByte('!') + b.writeByte('!') } - return b.String() + return b.hash() } -func getTypeAliasInstantiationKey(typeArguments []*Type, alias *TypeAlias) string { +func getTypeAliasInstantiationKey(typeArguments []*Type, alias *TypeAlias) xxh3.Uint128 { return getTypeInstantiationKey(typeArguments, alias, false) } -func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSignature bool) string { - var b KeyBuilder - b.WriteTypes(typeArguments) - b.WriteAlias(alias) +func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSignature bool) xxh3.Uint128 { + var b keyBuilder + b.writeTypes(typeArguments) + b.writeAlias(alias) if singleSignature { - b.WriteByte('!') + b.writeByte('!') } - return b.String() + return b.hash() } -func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) string { - var b KeyBuilder - b.WriteType(objectType) - b.WriteByte(',') - b.WriteType(indexType) - b.WriteByte(',') - b.WriteUint64(uint64(accessFlags)) - b.WriteAlias(alias) - return b.String() +func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) xxh3.Uint128 { + var b keyBuilder + b.writeType(objectType) + b.writeByte(',') + b.writeType(indexType) + b.writeByte(',') + hashWrite32(&b.h, accessFlags) + b.writeAlias(alias) + return b.hash() } -func getTemplateTypeKey(texts []string, types []*Type) string { - var b KeyBuilder - b.WriteTypes(types) - b.WriteByte('|') +func getTemplateTypeKey(texts []string, types []*Type) xxh3.Uint128 { + var b keyBuilder + b.writeTypes(types) + b.writeByte('|') for i, s := range texts { if i != 0 { - b.WriteByte(',') + b.writeByte(',') } - b.WriteInt(len(s)) + b.writeInt(len(s)) } - b.WriteByte('|') + b.writeByte('|') for _, s := range texts { - b.WriteString(s) + b.writeString(s) } - return b.String() + return b.hash() } -func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstraint bool) string { - var b KeyBuilder - b.WriteTypes(typeArguments) - b.WriteAlias(alias) +func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstraint bool) xxh3.Uint128 { + var b keyBuilder + b.writeTypes(typeArguments) + b.writeAlias(alias) if forConstraint { - b.WriteByte('!') + b.writeByte('!') } - return b.String() + return b.hash() } -func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) string { +func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) (xxh3.Uint128, bool) { if isIdentity && source.id > target.id { source, target = target, source } - var b KeyBuilder + var b keyBuilder var constrained bool if isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) { - constrained = b.WriteGenericTypeReferences(source, target, ignoreConstraints) + constrained = b.writeGenericTypeReferences(source, target, ignoreConstraints) } else { - b.WriteType(source) - b.WriteByte(',') - b.WriteType(target) + b.writeType(source) + b.writeByte(',') + b.writeType(target) } if intersectionState != IntersectionStateNone { - b.WriteByte(':') - b.WriteUint64(uint64(intersectionState)) + b.writeByte(':') + hashWrite32(&b.h, intersectionState) } - if constrained { - // We mark keys with type references that reference constrained type parameters such that we know - // to obtain and look for a "broadest equivalent key" in the cache. - b.WriteByte('*') - } - return b.String() + return b.hash(), constrained } -func getNodeListKey(nodes []*ast.Node) string { - var b KeyBuilder - for i, n := range nodes { - if i > 0 { - b.WriteByte(',') - } - b.WriteNode(n) +func getNodeListKey(nodes []*ast.Node) xxh3.Uint128 { + var b keyBuilder + b.writeInt(len(nodes)) + for _, n := range nodes { + b.writeNode(n) } - return b.String() + return b.hash() } func isTypeReferenceWithGenericArguments(t *Type) bool { @@ -21561,10 +21559,10 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl if index == -1 { c.pushActiveMapper(m) } - var b KeyBuilder - b.WriteType(t) - b.WriteAlias(alias) - key := b.String() + var b keyBuilder + b.writeType(t) + b.writeAlias(alias) + key := b.hash() cache := c.activeTypeMappersCaches[core.IfElse(index != -1, index, len(c.activeTypeMappersCaches)-1)] if cachedType, ok := cache[key]; ok { return cachedType @@ -21590,10 +21588,10 @@ func (c *Checker) pushActiveMapper(mapper *TypeMapper) { // 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) + c.activeTypeMappersCaches[lastIndex] = make(map[xxh3.Uint128]*Type, 1) } } else { - c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[string]*Type, 1)) + c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[xxh3.Uint128]*Type, 1)) } } @@ -21804,7 +21802,7 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type data := target.AsObjectType() key := getTypeInstantiationKey(typeArguments, newAlias, t.objectFlags&ObjectFlagsSingleSignatureType != 0) if data.instantiations == nil { - data.instantiations = make(map[string]*Type) + data.instantiations = make(map[xxh3.Uint128]*Type) data.instantiations[getTypeInstantiationKey(typeParameters, target.alias, false)] = target } result := data.instantiations[key] @@ -22425,12 +22423,12 @@ func (c *Checker) getESSymbolLikeTypeForNode(node *ast.Node) *Type { if symbol != nil { uniqueType := c.uniqueESSymbolTypes[symbol] if uniqueType == nil { - var b KeyBuilder + var b strings.Builder b.WriteString(ast.InternalSymbolNamePrefix) b.WriteByte('@') b.WriteString(symbol.Name) b.WriteByte('@') - b.WriteSymbol(symbol) + b.WriteString(strconv.FormatUint(uint64(ast.GetSymbolId(symbol)), 10)) uniqueType = c.newUniqueESSymbolType(symbol, b.String()) c.uniqueESSymbolTypes[symbol] = uniqueType } @@ -23220,7 +23218,7 @@ func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. links.typeParameters = typeParameters - links.instantiations = make(map[string]*Type) + links.instantiations = make(map[xxh3.Uint128]*Type) links.instantiations[getTypeListKey(typeParameters)] = t } if t == c.intrinsicMarkerType && symbol.Name == "BuiltinIteratorReturn" { @@ -23655,7 +23653,7 @@ func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { } links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil) if outerTypeParameters != nil { - root.instantiations = make(map[string]*Type) + root.instantiations = make(map[xxh3.Uint128]*Type) root.instantiations[getTypeListKey(outerTypeParameters)] = links.resolvedType } } @@ -24175,7 +24173,7 @@ func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonl d.thisType.AsTypeParameter().isThisType = true d.thisType.AsTypeParameter().constraint = t d.allTypeParameters = append(typeParameters, d.thisType) - d.instantiations = make(map[string]*Type) + d.instantiations = make(map[xxh3.Uint128]*Type) d.instantiations[getTypeListKey(d.TypeParameters())] = t d.target = t d.resolvedTypeArguments = d.TypeParameters() @@ -31001,7 +30999,7 @@ func (c *Checker) getApplicableIndexSymbol(t *Type, keyType *Type) *ast.Symbol { declarationList := core.MapNonNil(infos, func(info *IndexInfo) *ast.Node { return info.declaration }) nodeListId := getNodeListKey(declarationList) if indexSymbolLinks.filteredIndexSymbolCache == nil { - indexSymbolLinks.filteredIndexSymbolCache = make(map[string]*ast.Symbol) + indexSymbolLinks.filteredIndexSymbolCache = make(map[xxh3.Uint128]*ast.Symbol) } if result, ok := indexSymbolLinks.filteredIndexSymbolCache[nodeListId]; ok { return result diff --git a/internal/checker/flow.go b/internal/checker/flow.go index 8df30cad40..72f047994f 100644 --- a/internal/checker/flow.go +++ b/internal/checker/flow.go @@ -12,6 +12,7 @@ import ( "github.com/microsoft/typescript-go/internal/diagnostics" "github.com/microsoft/typescript-go/internal/evaluator" "github.com/microsoft/typescript-go/internal/scanner" + "github.com/zeebo/xxh3" ) type FlowType struct { @@ -40,7 +41,8 @@ type FlowState struct { declaredType *Type initialType *Type flowContainer *ast.Node - refKey string + refKey xxh3.Uint128 + refKeyValid bool depth int sharedFlowStart int reduceLabels []*ast.FlowReduceLabelData @@ -1288,10 +1290,10 @@ func (c *Checker) getUnionOrEvolvingArrayType(f *FlowState, types []*Type, subty } func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowType { - if f.refKey == "" { - f.refKey = c.getFlowReferenceKey(f) + if !f.refKeyValid { + f.refKey, f.refKeyValid = c.getFlowReferenceKey(f) } - if f.refKey == "?" { + if !f.refKeyValid { // No cache key is generated when binding patterns are in unnarrowable situations return FlowType{t: f.declaredType} } @@ -1610,16 +1612,16 @@ func (c *Checker) isMatchingReference(source *ast.Node, target *ast.Node) bool { // Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers // separated by dots). The key consists of the id of the symbol referenced by the // leftmost identifier followed by zero or more property names separated by dots. -// The result is an empty string if the reference isn't a dotted name. -func (c *Checker) getFlowReferenceKey(f *FlowState) string { - var b KeyBuilder +// The second return value indicates whether the reference is a valid dotted name. +func (c *Checker) getFlowReferenceKey(f *FlowState) (xxh3.Uint128, bool) { + var b keyBuilder if c.writeFlowCacheKey(&b, f.reference, f.declaredType, f.initialType, f.flowContainer) { - return b.String() + return b.hash(), true } - return "?" // Reference isn't a dotted name + return xxh3.Uint128{}, false // Reference isn't a dotted name } -func (c *Checker) writeFlowCacheKey(b *KeyBuilder, node *ast.Node, declaredType *Type, initialType *Type, flowContainer *ast.Node) bool { +func (c *Checker) writeFlowCacheKey(b *keyBuilder, node *ast.Node, declaredType *Type, initialType *Type, flowContainer *ast.Node) bool { switch node.Kind { case ast.KindIdentifier: if !ast.IsThisInTypeQuery(node) { @@ -1627,19 +1629,19 @@ func (c *Checker) writeFlowCacheKey(b *KeyBuilder, node *ast.Node, declaredType if symbol == c.unknownSymbol { return false } - b.WriteSymbol(symbol) + b.writeSymbol(symbol) } fallthrough case ast.KindThisKeyword: - b.WriteByte(':') - b.WriteType(declaredType) + b.writeByte(':') + b.writeType(declaredType) if initialType != declaredType { - b.WriteByte('=') - b.WriteType(initialType) + b.writeByte('=') + b.writeType(initialType) } if flowContainer != nil { - b.WriteByte('@') - b.WriteNode(flowContainer) + b.writeByte('@') + b.writeNode(flowContainer) } return true case ast.KindNonNullExpression, ast.KindParenthesizedExpression: @@ -1648,16 +1650,16 @@ func (c *Checker) writeFlowCacheKey(b *KeyBuilder, node *ast.Node, declaredType if !c.writeFlowCacheKey(b, node.AsQualifiedName().Left, declaredType, initialType, flowContainer) { return false } - b.WriteByte('.') - b.WriteString(node.AsQualifiedName().Right.Text()) + b.writeByte('.') + b.writeString(node.AsQualifiedName().Right.Text()) return true case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: if propName, ok := c.getAccessedPropertyName(node); ok { if !c.writeFlowCacheKey(b, node.Expression(), declaredType, initialType, flowContainer) { return false } - b.WriteByte('.') - b.WriteString(propName) + b.writeByte('.') + b.writeString(propName) return true } if ast.IsElementAccessExpression(node) && ast.IsIdentifier(node.AsElementAccessExpression().ArgumentExpression) { @@ -1666,16 +1668,16 @@ func (c *Checker) writeFlowCacheKey(b *KeyBuilder, node *ast.Node, declaredType if !c.writeFlowCacheKey(b, node.Expression(), declaredType, initialType, flowContainer) { return false } - b.WriteString(".@") - b.WriteSymbol(symbol) + b.writeString(".@") + b.writeSymbol(symbol) return true } } case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration: - b.WriteNode(node) - b.WriteByte('#') - b.WriteType(declaredType) + b.writeNode(node) + b.writeByte('#') + b.writeType(declaredType) return true } return false diff --git a/internal/checker/relater.go b/internal/checker/relater.go index db83af0745..e53390f2b3 100644 --- a/internal/checker/relater.go +++ b/internal/checker/relater.go @@ -13,6 +13,7 @@ import ( "github.com/microsoft/typescript-go/internal/diagnostics" "github.com/microsoft/typescript-go/internal/jsnum" "github.com/microsoft/typescript-go/internal/scanner" + "github.com/zeebo/xxh3" ) type SignatureCheckMode uint32 @@ -97,16 +98,16 @@ func asRecursionId[T *ast.Node | *ast.Symbol | *Type](value T) RecursionId { } type Relation struct { - results map[string]RelationComparisonResult + results map[xxh3.Uint128]RelationComparisonResult } -func (r *Relation) get(key string) RelationComparisonResult { +func (r *Relation) get(key xxh3.Uint128) RelationComparisonResult { return r.results[key] } -func (r *Relation) set(key string, result RelationComparisonResult) { +func (r *Relation) set(key xxh3.Uint128, result RelationComparisonResult) { if r.results == nil { - r.results = make(map[string]RelationComparisonResult) + r.results = make(map[xxh3.Uint128]RelationComparisonResult) } r.results[key] = result } @@ -191,7 +192,8 @@ func (c *Checker) isTypeRelatedTo(source *Type, target *Type, relation *Relation } } if source.flags&TypeFlagsObject != 0 && target.flags&TypeFlagsObject != 0 { - related := relation.get(getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false)) + id, _ := getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false) + related := relation.get(id) if related != RelationComparisonResultNone { return related&RelationComparisonResultSucceeded != 0 } @@ -370,7 +372,7 @@ func (c *Checker) checkTypeRelatedToEx( result := r.isRelatedToEx(source, target, RecursionFlagsBoth, errorNode != nil /*reportErrors*/, headMessage, IntersectionStateNone) if r.overflow { // Record this relation as having failed such that we don't attempt the overflowing operation again. - id := getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false /*ignoreConstraints*/) + id, _ := getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false /*ignoreConstraints*/) relation.set(id, RelationComparisonResultFailed|core.IfElse(r.relationCount <= 0, RelationComparisonResultComplexityOverflow, RelationComparisonResultStackDepthOverflow)) message := core.IfElse(r.relationCount <= 0, diagnostics.Excessive_complexity_comparing_types_0_and_1, diagnostics.Excessive_stack_depth_comparing_types_0_and_1) if errorNode == nil { @@ -2504,8 +2506,8 @@ type Relater struct { errorNode *ast.Node errorChain *ErrorChain relatedInfo []*ast.Diagnostic - maybeKeys []string - maybeKeysSet collections.Set[string] + maybeKeys []xxh3.Uint128 + maybeKeysSet collections.Set[xxh3.Uint128] sourceStack []*Type targetStack []*Type maybeCount int @@ -3014,7 +3016,7 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError if r.overflow { return TernaryFalse } - id := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, false /*ignoreConstraints*/) + id, constrained := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, false /*ignoreConstraints*/) if entry := r.relation.get(id); entry != RelationComparisonResultNone { if reportErrors && entry&RelationComparisonResultFailed != 0 && entry&RelationComparisonResultOverflow == 0 { // We are elaborating errors and the cached result is a failure not due to a comparison overflow, @@ -3041,11 +3043,11 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError if r.maybeKeysSet.Has(id) { return TernaryMaybe } - // A key that ends with "*" is an indication that we have type references that reference constrained + // A constrained key indicates that we have type references that reference constrained // type parameters. For such keys we also check against the key we would have gotten if all type parameters // were unconstrained. - if strings.HasSuffix(id, "*") { - broadestEquivalentId := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, true /*ignoreConstraints*/) + if constrained { + broadestEquivalentId, _ := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, true /*ignoreConstraints*/) if r.maybeKeysSet.Has(broadestEquivalentId) { return TernaryMaybe } diff --git a/internal/checker/types.go b/internal/checker/types.go index 0c2ff33a41..de43edd480 100644 --- a/internal/checker/types.go +++ b/internal/checker/types.go @@ -7,6 +7,7 @@ import ( "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/evaluator" + "github.com/zeebo/xxh3" ) //go:generate go tool golang.org/x/tools/cmd/stringer -type=SignatureKind -output=stringer_generated.go @@ -183,8 +184,8 @@ type ExportTypeLinks struct { type TypeAliasLinks struct { declaredType *Type - typeParameters []*Type // Type parameters of type alias (undefined if non-generic) - instantiations map[string]*Type // Instantiations of generic type alias (undefined if non-generic) + typeParameters []*Type // Type parameters of type alias (undefined if non-generic) + instantiations map[xxh3.Uint128]*Type // Instantiations of generic type alias (undefined if non-generic) isConstructorDeclaredProperty bool } @@ -261,7 +262,7 @@ const ( ) type IndexSymbolLinks struct { - filteredIndexSymbolCache map[string]*ast.Symbol // Symbol with applicable declarations + filteredIndexSymbolCache map[xxh3.Uint128]*ast.Symbol // Symbol with applicable declarations } type MarkedAssignmentSymbolLinks struct { @@ -849,9 +850,9 @@ func (t *StructuredType) Properties() []*ast.Symbol { type ObjectType struct { StructuredType - target *Type // Target of instantiated type - mapper *TypeMapper // Type mapper for instantiated type - instantiations map[string]*Type // Map of type instantiations + target *Type // Target of instantiated type + mapper *TypeMapper // Type mapper for instantiated type + instantiations map[xxh3.Uint128]*Type // Map of type instantiations } func (t *ObjectType) AsObjectType() *ObjectType { return t } @@ -1081,7 +1082,7 @@ type ConditionalRoot struct { isDistributive bool inferTypeParameters []*Type outerTypeParameters []*Type - instantiations map[string]*Type + instantiations map[xxh3.Uint128]*Type alias *TypeAlias }