diff --git a/packages/typescript/enums/objectFlags.d.ts b/packages/typescript/enums/objectFlags.d.ts index 45303ee1ce1a4..fab4ac64a875b 100644 --- a/packages/typescript/enums/objectFlags.d.ts +++ b/packages/typescript/enums/objectFlags.d.ts @@ -34,7 +34,6 @@ export declare enum ObjectFlags { ContainsSpread = 1 << 22, ObjectRestType = 1 << 23, IsClassInstanceClone = 1 << 26, - IdenticalBaseTypeCalculated = 1 << 27, IdenticalBaseTypeExists = 1 << 28, FromTypeNode = 1 << 29, IsGenericTypeComputed = 1 << 22, diff --git a/packages/typescript/enums/objectFlags.js b/packages/typescript/enums/objectFlags.js index 380ea72af1c7d..989b9db5c5681 100644 --- a/packages/typescript/enums/objectFlags.js +++ b/packages/typescript/enums/objectFlags.js @@ -34,7 +34,6 @@ export var ObjectFlags; ObjectFlags[ObjectFlags["ContainsSpread"] = 4194304] = "ContainsSpread"; ObjectFlags[ObjectFlags["ObjectRestType"] = 8388608] = "ObjectRestType"; ObjectFlags[ObjectFlags["IsClassInstanceClone"] = 67108864] = "IsClassInstanceClone"; - ObjectFlags[ObjectFlags["IdenticalBaseTypeCalculated"] = 134217728] = "IdenticalBaseTypeCalculated"; ObjectFlags[ObjectFlags["IdenticalBaseTypeExists"] = 268435456] = "IdenticalBaseTypeExists"; ObjectFlags[ObjectFlags["FromTypeNode"] = 536870912] = "FromTypeNode"; ObjectFlags[ObjectFlags["IsGenericTypeComputed"] = 4194304] = "IsGenericTypeComputed"; diff --git a/tsc/internal/api/enum_values_generated.go b/tsc/internal/api/enum_values_generated.go index 1697a479539a5..b223dab0403fb 100644 --- a/tsc/internal/api/enum_values_generated.go +++ b/tsc/internal/api/enum_values_generated.go @@ -242,7 +242,6 @@ func main() { "ContainsSpread": toInt32(checker.ObjectFlagsContainsSpread), "ObjectRestType": toInt32(checker.ObjectFlagsObjectRestType), "IsClassInstanceClone": toInt32(checker.ObjectFlagsIsClassInstanceClone), - "IdenticalBaseTypeCalculated": toInt32(checker.ObjectFlagsIdenticalBaseTypeCalculated), "IdenticalBaseTypeExists": toInt32(checker.ObjectFlagsIdenticalBaseTypeExists), "FromTypeNode": toInt32(checker.ObjectFlagsFromTypeNode), "IsGenericTypeComputed": toInt32(checker.ObjectFlagsIsGenericTypeComputed), diff --git a/tsc/internal/ast/symbol.go b/tsc/internal/ast/symbol.go index 7c8e60e921d55..28862ff494d3d 100644 --- a/tsc/internal/ast/symbol.go +++ b/tsc/internal/ast/symbol.go @@ -223,6 +223,11 @@ func (m *SymbolMap) SymbolAt(i int) *Symbol { return symbol } +// At returns the name and symbol of the i-th entry, in insertion order. +func (m *SymbolMap) At(i int) (string, *Symbol) { + return m.KeyAt(i), m.SymbolAt(i) +} + func (m *SymbolMap) Find(key string) int { if m == nil { return -1 diff --git a/tsc/internal/ast/utilities.go b/tsc/internal/ast/utilities.go index f178903372dc2..d029159e971d3 100644 --- a/tsc/internal/ast/utilities.go +++ b/tsc/internal/ast/utilities.go @@ -33,6 +33,16 @@ func GetNodeId(node *Node) NodeId { return NodeId(id) } +// PeekNodeId returns the id of node, or 0 if it has none yet. +func PeekNodeId(node *Node) NodeId { + return NodeId(node.id.Load()) +} + +// PeekSymbolId returns the id of symbol, or 0 if it has none yet. +func PeekSymbolId(symbol *Symbol) SymbolId { + return SymbolId(symbol.id.Load()) +} + func GetSymbolId(symbol *Symbol) SymbolId { id := symbol.id.Load() if id == 0 { diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index c61c878843343..40177f2f76725 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -1,6 +1,7 @@ package checker import ( + "cmp" "context" "encoding/binary" "fmt" @@ -66,12 +67,38 @@ const ( TypeSystemPropertyNameInitializerIsUndefined TypeSystemPropertyNameAliasTarget TypeSystemPropertyNameComputedPropertyName + TypeSystemPropertyNameContextChecked + TypeSystemPropertyNameVariances + TypeSystemPropertyNameStructuredMembers + TypeSystemPropertyNameSimplifiedForReading + TypeSystemPropertyNameSimplifiedForWriting + TypeSystemPropertyNameDefaultType + TypeSystemPropertyNameAwaitedType + TypeSystemPropertyNameExplicitType + TypeSystemPropertyNameBindingElementParent + TypeSystemPropertyNameCallResolution + TypeSystemPropertyNameAssertionOperand + TypeSystemPropertyNamePatternInitializer + TypeSystemPropertyNameContextualBindingPattern ) type TypeResolution struct { - target TypeSystemEntity + target TypeSystemEntity + // canonical is the member of a cycle headed by this resolution that is + // resolved first; see observeCircularHead. + canonical TypeSystemEntity + // participant records that the resolution is in cycleParticipants. + participant bool + memo *memoHeader propertyName TypeSystemPropertyName result bool + // restartHidden records that a cycle entered below this resolution of a + // module table passes through it; see resolveModuleTableMembers. + restartHidden bool + // outerSelf is one more than the index in resolutions of the outermost + // resolution of the same property of target hidden below this one, or 0; + // see popTypeResolution. + outerSelf int32 } // ContextualInfo @@ -243,14 +270,6 @@ type PropertiesTypesKey struct { includeOrigin bool } -// NonExistentPropertyKey - -type NonExistentPropertyKey struct { - propNode *ast.Node - containingType *Type - isUncheckedJS bool -} - // FlowLoopKey type FlowLoopKey struct { @@ -258,6 +277,10 @@ type FlowLoopKey struct { refKey CacheHashKey } +type flowLoopCacheEntry struct { + t *Type +} + type FlowLoopInfo struct { key FlowLoopKey types []*Type @@ -549,8 +572,21 @@ type WideningContext struct { } type VarianceStackEntry struct { - symbol *ast.Symbol - typeParameters []*Type + symbol *ast.Symbol + resolution int // Index in Checker.resolutions of the resolution of the measurement + // The lowest index in the variance stack of a measurement in progress + // that this one or one it reached needed, and the smallest type in the + // circularity so far, as in Tarjan's algorithm for strongly connected + // components. + low int + min *ast.Symbol + minTypeParameters []*Type + discardPoint // When the measurement began + // The measurement is being repeated because the measurement from the + // canonical entry of its circularity did not reach it, so it is not + // superseded again. + remeasuring bool + provisionalStart int // Length of Checker.varianceProvisional when the measurement began } type ResolvingObjectType struct { @@ -593,102 +629,35 @@ type Host interface { var nextCheckerID atomic.Uint32 +// Checker is a checker: the per-computation state (resolution stacks, scratch +// buffers, allocation arenas, and closures bound to this checker) layered +// over a memoStore that holds all memoized, shareable state. Multiple +// checkers may share one store; see Clone. type Checker struct { - id uint32 - program Program - compilerOptions *core.CompilerOptions - files []*ast.SourceFile - fileIndexMap map[*ast.SourceFile]int - compareSymbols func(*ast.Symbol, *ast.Symbol) int - compareSymbolChains func([]*ast.Symbol, []*ast.Symbol) int - TypeCount uint32 - SymbolCount uint32 - SignatureCount uint32 - TotalInstantiationCount uint32 - instantiationCount uint32 - memberLayoutCount uint32 - instantiationStack []*Type - conditionalConstraintDepth uint32 - inlineLevel int - serializationLevel int - currentNode *ast.Node - varianceTypeParameter *Type - languageVersion core.ScriptTarget - moduleKind core.ModuleKind - moduleResolutionKind core.ModuleResolutionKind + *memoStore + compareSymbols func(*ast.Symbol, *ast.Symbol) int + compareSymbolChains func([]*ast.Symbol, []*ast.Symbol) int + awaitedTypeErrors uint32 // Errors computing awaited types have reported or would report; see getAwaitedTypeNoAliasEx + resolutionSerials uint32 + resolutions []resolution + instantiationStack []*Type + conditionalConstraintDepth depthLimit + inlineLevel depthLimit + serializationLevel depthLimit + currentNode *ast.Node + currentNodeResolution int // 1-based index in resolutions of the resolution of currentNode, or 0; see enterCurrentNode + querySave savedCurrentNode // Saved by the query in progress made outside of checking; see startEffects + queryRoot int // 1-based index in resolutions of the computation of that query, or 0 + varianceTypeParameter *Type + // The parent last looked up in parentLateBindableKinds and its kinds, as + // members of one parent are often looked up together. + lastLateBindableParent *ast.Symbol + lastLateBindableKinds uint8 isInferencePartiallyBlocked bool - legacyDecorators bool - emitStandardClassFields bool - strictNullChecks bool - strictFunctionTypes bool - strictBindCallApply bool - strictPropertyInitialization bool - strictBuiltinIteratorReturn bool - noImplicitAny bool - noImplicitThis bool - useUnknownInCatchVariables bool - exactOptionalPropertyTypes bool - canCollectSymbolAliasAccessibilityData bool wasCanceled bool - arrayVariances []VarianceFlags - globals ast.SymbolTable evaluate evaluator.Evaluator - stringLiteralTypes map[string]*Type - numberLiteralTypes map[jsnum.Number]*Type - nanType *Type - bigintLiteralTypes map[jsnum.PseudoBigInt]*Type - enumLiteralTypes map[EnumLiteralKey]*Type - enumNaNLiteralTypes map[*ast.Symbol]*Type - indexedAccessTypes map[CacheHashKey]*Type - templateLiteralTypes map[CacheHashKey]*Type - stringMappingTypes map[StringMappingKey]*Type - uniqueESSymbolTypes map[*ast.Symbol]*Type - thisExpandoKinds map[*ast.Symbol]thisAssignmentDeclarationKind - thisExpandoLocations map[*ast.Symbol]*ast.Node - subtypeReductionCache map[CacheHashKey][]*Type - cachedTypes map[CachedTypeKey]*Type - cachedSignatures map[CachedSignatureKey]*Signature - undefinedProperties map[*ast.Symbol]*ast.Symbol - narrowedTypes map[NarrowedTypeKey]*Type - assignmentReducedTypes map[AssignmentReducedKey]*Type - discriminatedContextualTypes map[DiscriminatedContextualTypeKey]*Type - instantiationExpressionTypes map[InstantiationExpressionKey]*Type - substitutionTypes map[SubstitutionTypeKey]*Type - reverseMappedCache map[ReverseMappedTypeKey]*Type - reverseHomomorphicMappedCache map[ReverseMappedTypeKey]*Type - iterationTypesCache map[IterationTypesKey]IterationTypes - memberLayoutCache map[CacheHashKey]*MemberLayout - markerTypes collections.Set[*Type] - resolvingExplicitTypeOfSymbol collections.Set[*ast.Symbol] - resolvingAssertionOperands collections.Set[*ast.Node] - constructorOrFunctionChecked collections.Set[*ast.Symbol] - undefinedSymbol *ast.Symbol - argumentsSymbol *ast.Symbol - requireSymbol *ast.Symbol - unknownSymbol *ast.Symbol - unresolvedSymbols map[string]*ast.Symbol - errorTypes map[CacheHashKey]*Type - moduleSymbols map[*ast.Node]*ast.Symbol - globalThisSymbol *ast.Symbol - symbolTableAliasCache map[symbolTableID][]*ast.Symbol - classExpressionNameTables map[ast.NodeId]ast.SymbolTable 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[CacheHashKey]*Type - unionTypes map[CacheHashKey]*Type - objectLiteralTypes map[CacheHashKey]*Type - spreadTypes map[CacheHashKey]*Type - objectContentTypes map[CacheHashKey]*Type - spreadSymbols map[spreadSymbolKey]*ast.Symbol - symbolsWithType map[symbolWithTypeKey]*ast.Symbol - paddedProperties map[symbolWithTypeKey]*ast.Symbol - bindingPatternTypes map[CacheHashKey]*Type - unionOfUnionTypes map[UnionOfUnionKey]*Type - intersectionTypes map[CacheHashKey]*Type - intersectionContentTypes map[CacheHashKey]*Type - propertiesTypes map[PropertiesTypesKey]*Type - diagnostics ast.DiagnosticsCollection - suggestionDiagnostics ast.DiagnosticsCollection symbolArena core.Arena[ast.Symbol] symbolWithDataArena core.Arena[ast.SymbolWithData] symbolExtraArena ast.SymbolExtraArena @@ -715,134 +684,38 @@ type Checker struct { layoutBaseIndices []int32 freeTypeSets *temporaryTypeSet freePropertyCounts *temporaryPropertyCounts - mergedSymbols symbolLinkStore[*ast.Symbol] - mergedExportsChecked collections.Set[*ast.Symbol] factory ast.NodeFactory - nodeLinks core.LinkStore[*ast.Node, NodeLinks] - signatureLinks nodeLinkStore[SignatureLinks] - symbolNodeLinks nodeLinkStore[SymbolNodeLinks] - typeNodeLinks nodeLinkStore[TypeNodeLinks] - enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] - arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks] - switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] - jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] - computedNameLinks core.LinkStore[*ast.Node, ComputedNameNodeLinks] - symbolReferenceLinks core.LinkStore[*ast.Symbol, SymbolReferenceLinks] - valueSymbolLinks symbolLinkStore[ValueSymbolLinks] - mappedSymbolLinks core.LinkStore[*ast.Symbol, MappedSymbolLinks] - deferredSymbolLinks core.LinkStore[*ast.Symbol, DeferredSymbolLinks] - aliasSymbolLinks core.LinkStore[*ast.Symbol, AliasSymbolLinks] - moduleSymbolLinks core.LinkStore[*ast.Symbol, ModuleSymbolLinks] - lateBoundLinks core.LinkStore[*ast.Symbol, LateBoundLinks] - exportTypeLinks core.LinkStore[*ast.Symbol, ExportTypeLinks] - membersAndExportsLinks core.LinkStore[*ast.Symbol, MembersAndExportsLinks] - typeAliasLinks core.LinkStore[*ast.Symbol, TypeAliasLinks] - declaredTypeLinks core.LinkStore[*ast.Symbol, DeclaredTypeLinks] - spreadLinks core.LinkStore[*ast.Symbol, SpreadLinks] - varianceLinks core.LinkStore[*ast.Symbol, VarianceLinks] - ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks] - markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks] - symbolContainerLinks core.LinkStore[*ast.Symbol, ContainingSymbolLinks] - externalModuleContainers *externalModuleContainerIndex - sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] regExpScanner *scanner.Scanner - patternForType map[*Type]*ast.Node - contextFreeTypes map[*ast.Node]*Type - anyType *Type - autoType *Type - wildcardType *Type - blockedStringType *Type - errorType *Type - unresolvedType *Type - nonInferrableAnyType *Type - intrinsicMarkerType *Type - unknownType *Type - undefinedType *Type - undefinedWideningType *Type - missingType *Type - undefinedOrMissingType *Type - optionalType *Type - nullType *Type - nullWideningType *Type - stringType *Type - numberType *Type - bigintType *Type - regularFalseType *Type - falseType *Type - regularTrueType *Type - trueType *Type - booleanType *Type - esSymbolType *Type - voidType *Type - neverType *Type - silentNeverType *Type - implicitNeverType *Type - unreachableNeverType *Type - nonPrimitiveType *Type - stringOrNumberType *Type - stringNumberSymbolType *Type - numberOrBigIntType *Type - templateConstraintType *Type - numericStringType *Type - uniqueLiteralType *Type uniqueLiteralMapper *TypeMapper reliabilityFlags RelationComparisonResult reportUnreliableMapper *TypeMapper reportUnmeasurableMapper *TypeMapper restrictiveMapper *TypeMapper permissiveMapper *TypeMapper - emptyObjectType *Type - emptyJsxObjectType *Type - emptyFreshJsxObjectType *Type - emptyTypeLiteralType *Type - unknownEmptyObjectType *Type - unknownUnionType *Type - emptyGenericType *Type - anyFunctionType *Type - noConstraintType *Type - circularConstraintType *Type - resolvingDefaultType *Type - markerSuperType *Type - markerSubType *Type - markerOtherType *Type - markerSuperTypeForCheck *Type - markerSubTypeForCheck *Type - noTypePredicate *TypePredicate - anySignature *Signature - unknownSignature *Signature - resolvingSignature *Signature - silentNeverSignature *Signature - cachedArgumentsReferenced map[*ast.Node]bool - enumNumberIndexInfo *IndexInfo - anyBaseTypeIndexInfo *IndexInfo - patternAmbientModules []*ast.PatternAmbientModule - patternAmbientModuleAugmentations ast.SymbolTable - patternAmbientModuleAugmentationTargets ast.SymbolTable - moduleImportAttributesTypes map[*ast.Symbol]*Type - globalObjectType *Type - globalFunctionType *Type - globalCallableFunctionType *Type - globalNewableFunctionType *Type - globalArrayType *Type - globalReadonlyArrayType *Type - globalStringType *Type - globalNumberType *Type - globalBooleanType *Type - globalRegExpType *Type - globalThisType *Type - anyArrayType *Type - autoArrayType *Type - anyReadonlyArrayType *Type - deferredGlobalImportMetaExpressionType *Type - contextualBindingPatterns []*ast.Node - emptyStringType *Type - zeroType *Type - zeroBigIntType *Type - typeofType *Type - typeResolutions []TypeResolution - resolutionStart int + resolving resolvingTable // Index in resolutions of the outermost computation of each key in progress + typeResolutionTop int // 1-based index in resolutions of the innermost resolution, or 0 + resolutionStart int // Index in resolutions below which resolutions are not searched for cycles + visibleStart int // Index in resolutions below which nothing in progress is visible; see yieldCycle + cycleYields []cycleYield // Canonical recomputations in progress; see yieldCycle + circularityStart int // 1-based index in resolutions of the outermost cycle in progress, or 0; see memo.go + currentNodeErrors []*currentNodeError // Errors reported at the current node while collecting; see startCurrentNodeErrors + currentNodeErrorCollectors int // Number of computations collecting currentNodeErrors + currentNodeErrorsCyclic bool // Whether a cycle was observed while collecting currentNodeErrors; see endCurrentNodeErrors + discardLog []discardEntry // Restores what was cached while anything may be discarded; see "Discard log" in memo.go + circularityMark int // Length of discard log when the cycle in progress was detected + innerCycles []innerCycle // Cycles detected inside the one in progress; see extendCircularity + flowDepthFailures map[FlowLoopKey]int // Largest depth budget known to be too small for a flow walk from a node; see getTypeAtFlowNode + observedHead int32 // 1 + index in resolutions of the lowest head of a cycle observed by the innermost type resolution in progress, or 0 + cycleParticipants []*memoHeader // Computations that observed a cycle in progress + effectsMarkDepth int // 1 + resolution index of the innermost effects mark that may be discarded, or 0 varianceStack []VarianceStackEntry - callResolutionStack []*ast.Node + varianceCycleRoot int // 1 + index in varianceStack of the outermost measurement re-entered in progress, or 0; see getRecursiveVariances + varianceSuperseded bool // The measurement at the bottom of varianceStack is certain to be discarded; see getRecursiveVariances + varianceProvisional []*ast.Symbol // Types measured in a circularity with a measurement in progress; see isGuessedVariance + flushBuf []*memoState + resumeBuf []int + deferredAttach []deferredAttachment // See attachState + recordingFromCheck bool // A query begun in a check is in progress; see recordFromCheck apparentArgumentCount *int lastGetCombinedNodeFlagsNode *ast.Node lastGetCombinedNodeFlagsResult ast.NodeFlags @@ -850,31 +723,24 @@ type Checker struct { lastGetCombinedModifierFlagsResult ast.ModifierFlags freeinferenceState *InferenceState freeFlowState *FlowState - flowLoopCache map[FlowLoopKey]*Type flowLoopStack []FlowLoopInfo + signaturesResolving []*SignatureLinks // Calls whose resolution is in progress + visibleSignatures int // Index in signaturesResolving below which nothing is visible; see yieldCycle sharedFlows []SharedFlow antecedentTypes []*Type flowInvocationCount int - flowTypeCache map[*ast.Node]*Type + flowTypeCache map[*ast.Node]*Type // Types of expressions within the innermost loop analysis lastFlowNode *ast.FlowNode lastFlowNodeReachable bool - flowNodeReachable map[*ast.FlowNode]bool - flowNodePostSuper map[*ast.FlowNode]bool renamedBindingElementsInTypes []*ast.Node contextualInfos []ContextualInfo inferenceContextInfos []InferenceContextInfo - awaitedTypeStack []*Type reverseMappedSourceStack []*Type reverseMappedTargetStack []*Type resolvingObjectTypeStack []ResolvingObjectType reverseExpandingFlags ExpandingFlags + reverseMappedStart int32 freeRelater *Relater - subtypeRelation *Relation - strictSubtypeRelation *Relation - assignableRelation *Relation - comparableRelation *Relation - identityRelation *Relation - enumRelation map[EnumRelationKey]RelationComparisonResult getGlobalESSymbolType func() *Type getGlobalBigIntType func() *Type getGlobalImportMetaType func() *Type @@ -935,38 +801,275 @@ type Checker struct { isStringIndexSignatureOnlyType func(*Type) bool markNodeAssignments func(*ast.Node) bool compareTypesAssignable TypeComparer - emitResolverLinks EmitResolverLinks - _jsxNamespace string - _jsxFactoryEntity *ast.Node skipDirectInferenceNodes collections.Set[*ast.Node] ctx context.Context - packagesMap map[string]bool activeMappers []*TypeMapper - activeTypeMappersCaches []clearableMap[*Type] - ambientModulesOnce sync.Once - ambientModules []*ast.Symbol + activeTypeMappersCaches []clearableMap[activeMapperCacheEntry] withinUnreachableCode bool - reportedUnreachableNodes collections.Set[*ast.Node] - nonExistentProperties collections.Set[NonExistentPropertyKey] - deferredDiagnosticCallbacks []func() typeToStringNodebuilder *NodeBuilder - - mu sync.Mutex - tracer *Tracer // Optional tracer for trace events and type recording (for --generateTrace) + tracer *Tracer // Optional tracer for trace events and type recording (for --generateTrace) + checking bool // Whether side effects such as diagnostics are applied; see memo.go + observingMemos bool // See syncObserving + instantiationCount uint32 // Instantiations since the start of the current statement, expression or outermost computation; see "Instantiation limits" + memoTop int // 1 + index in resolutions of the innermost computation recording effects, or 0; see memo.go +} + +// memoStore holds the memoized state of a checker: interned types, links, +// relation caches, and collected diagnostics. Every checker sharing a store +// observes the same results. +type memoStore struct { + root *Checker // The checker that created this store; owns type back-pointers. + id uint32 + program Program + compilerOptions *core.CompilerOptions + files []*ast.SourceFile + fileIndexMap map[*ast.SourceFile]int + TypeCount uint32 + memberLayoutCount uint32 + SymbolCount uint32 + SignatureCount uint32 + TotalInstantiationCount uint32 + languageVersion core.ScriptTarget + moduleKind core.ModuleKind + moduleResolutionKind core.ModuleResolutionKind + legacyDecorators bool + emitStandardClassFields bool + strictNullChecks bool + strictFunctionTypes bool + strictBindCallApply bool + strictPropertyInitialization bool + strictBuiltinIteratorReturn bool + noImplicitAny bool + noImplicitThis bool + useUnknownInCatchVariables bool + exactOptionalPropertyTypes bool + canCollectSymbolAliasAccessibilityData bool + arrayVariances []VarianceFlags + globals ast.SymbolTable + globalIDs map[globalKey]globalID + globalTypes []memo[resolvedGlobal[*Type]] // Indexed by globalID + globalSymbols []memo[resolvedGlobal[*ast.Symbol]] // Indexed by globalID + stringLiteralTypes internMap[string, *Type] + numberLiteralTypes internMap[jsnum.Number, *Type] + nanType *Type + bigintLiteralTypes internMap[jsnum.PseudoBigInt, *Type] + enumLiteralTypes internMap[EnumLiteralKey, *Type] + enumNaNLiteralTypes internMap[*ast.Symbol, *Type] + indexedAccessTypes internMap[CacheHashKey, *Type] + templateLiteralTypes internMap[CacheHashKey, *Type] + stringMappingTypes internMap[StringMappingKey, *Type] + uniqueESSymbolTypes internMap[*ast.Symbol, *Type] + thisExpandos internMap[*ast.Symbol, thisExpando] + subtypeReductionCache memoMap[CacheHashKey, []*Type] + cachedTypes memoMap[CachedTypeKey, *Type] + cachedSignatures memoMap[CachedSignatureKey, *Signature] + undefinedProperties memoMap[*ast.Symbol, *ast.Symbol] + narrowedTypes memoMap[NarrowedTypeKey, *Type] + assignmentReducedTypes memoMap[AssignmentReducedKey, *Type] + discriminatedContextualTypes memoMap[DiscriminatedContextualTypeKey, *Type] + instantiationExpressionTypes internMap[InstantiationExpressionKey, *instantiationExpressionEntry] + substitutionTypes internMap[SubstitutionTypeKey, *Type] + reverseMappedCache memoMap[ReverseMappedTypeKey, *Type] + reverseHomomorphicMappedCache memoMap[ReverseMappedTypeKey, *Type] + iterationTypesCache memoMap[IterationTypesKey, IterationTypes] + memberLayoutCache memoMap[CacheHashKey, *MemberLayout] + constructorOrFunctionChecked memoSet[*ast.Symbol] + typeParametersChecked memoSet[*ast.Symbol] + indexSignaturesChecked memoSet[*ast.Symbol] + interfaceChecked memoSet[*ast.Symbol] + enumChecked memoSet[*ast.Symbol] + moduleExportsChecked memoSet[*ast.Symbol] + assignmentsMarked memoSet[*ast.Node] // Functions and source files whose assignments have been marked + undefinedSymbol *ast.Symbol + argumentsSymbol *ast.Symbol + requireSymbol *ast.Symbol + unknownSymbol *ast.Symbol + unresolvedSymbols internMap[string, *ast.Symbol] + errorTypes internMap[CacheHashKey, *Type] + globalThisSymbol *ast.Symbol + symbolTableAliasCache internMap[symbolTableID, []*ast.Symbol] + classExpressionNameTables internMap[ast.NodeId, ast.SymbolTable] + tupleTypes internMap[CacheHashKey, *Type] + unionTypes internMap[CacheHashKey, *Type] + unionOfUnionTypes memoMap[UnionOfUnionKey, *Type] + intersectionTypes memoMap[CacheHashKey, *Type] + objectLiteralTypes internMap[*ast.Node, []objectLiteralType] + spreadTypes memoMap[CacheHashKey, *Type] + objectContentTypes internMap[CacheHashKey, *Type] + spreadSymbols memoMap[spreadSymbolKey, *ast.Symbol] + symbolsWithType internMap[symbolWithTypeKey, *ast.Symbol] + paddedProperties internMap[symbolWithTypeKey, *ast.Symbol] + bindingPatternTypes internMap[CacheHashKey, *Type] + intersectionContentTypes internMap[CacheHashKey, *Type] + propertiesTypes memoMap[PropertiesTypesKey, *Type] + diagnostics ast.DiagnosticsCollection + suggestionDiagnostics ast.DiagnosticsCollection + mergedSymbols symbolLinkStore[*ast.Symbol] + lateMergedSymbols memoMap[*ast.Symbol, *ast.Symbol] + // getLateBindableKinds of the merged symbol of a parent of members, with + // lateBindableKnown set once known; see getLateMergedSymbol. + parentLateBindableKinds symbolLinkStore[uint8] + // mergesFrozen is set once global merging is done; mergedSymbols is + // immutable from then on. + mergesFrozen bool + mergedExportsChecked memoSet[*ast.Symbol] + nodeLinks core.LinkStore[*ast.Node, NodeLinks] + signatureLinks nodeLinkStore[SignatureLinks] + symbolNodeLinks nodeLinkStore[SymbolNodeLinks] + typeNodeLinks nodeLinkStore[TypeNodeLinks] + enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] + arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks] + switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] + jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] + computedNameLinks core.LinkStore[*ast.Node, ComputedNameNodeLinks] + symbolReferenceLinks core.LinkStore[*ast.Symbol, SymbolReferenceLinks] + valueSymbolLinks symbolLinkStore[ValueSymbolLinks] + mappedSymbolLinks core.LinkStore[*ast.Symbol, MappedSymbolLinks] + deferredSymbolLinks core.LinkStore[*ast.Symbol, DeferredSymbolLinks] + aliasSymbolLinks core.LinkStore[*ast.Symbol, AliasSymbolLinks] + moduleSymbolLinks core.LinkStore[*ast.Symbol, ModuleSymbolLinks] + lateBoundLinks core.LinkStore[*ast.Symbol, LateBoundLinks] + exportTypeLinks core.LinkStore[*ast.Symbol, ExportTypeLinks] + membersAndExportsLinks core.LinkStore[*ast.Symbol, MembersAndExportsLinks] + typeAliasLinks core.LinkStore[*ast.Symbol, TypeAliasLinks] + declaredTypeLinks core.LinkStore[*ast.Symbol, DeclaredTypeLinks] + spreadLinks core.LinkStore[*ast.Symbol, SpreadLinks] + varianceLinks symbolLinkStore[VarianceLinks] + ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks] + markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks] + importedContainingModules memoMap[symbolFileKey, []*ast.Symbol] // Modules imported by a file that export a symbol + extendedContainers memoMap[*ast.Symbol, []*ast.Symbol] // Containers (other than the parent) which a symbol is aliased in + accessibleChains memoMap[accessibleChainCacheKey, []*ast.Symbol] // Accessible symbol chains by location + externalModuleContainers memo[*externalModuleContainerIndex] + sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] + patternForType internMap[*Type, *ast.Node] + contextFreeTypes memoMap[*ast.Node, *Type] + anyType *Type + autoType *Type + wildcardType *Type + blockedStringType *Type + errorType *Type + unresolvedType *Type + nonInferrableAnyType *Type + intrinsicMarkerType *Type + unknownType *Type + undefinedType *Type + undefinedWideningType *Type + missingType *Type + undefinedOrMissingType *Type + optionalType *Type + nullType *Type + nullWideningType *Type + stringType *Type + numberType *Type + bigintType *Type + regularFalseType *Type + falseType *Type + regularTrueType *Type + trueType *Type + booleanType *Type + esSymbolType *Type + voidType *Type + neverType *Type + silentNeverType *Type + implicitNeverType *Type + unreachableNeverType *Type + nonPrimitiveType *Type + stringOrNumberType *Type + stringNumberSymbolType *Type + numberOrBigIntType *Type + templateConstraintType *Type + numericStringType *Type + uniqueLiteralType *Type + emptyObjectType *Type + emptyJsxObjectType *Type + emptyFreshJsxObjectType *Type + emptyTypeLiteralType *Type + unknownEmptyObjectType *Type + unknownUnionType *Type + emptyGenericType *Type + anyFunctionType *Type + noConstraintType *Type + circularConstraintType *Type + resolvingDefaultType *Type + markerSuperType *Type + markerSubType *Type + markerOtherType *Type + markerSuperTypeForCheck *Type + markerSubTypeForCheck *Type + noTypePredicate *TypePredicate + anySignature *Signature + unknownSignature *Signature + resolvingSignature *Signature + silentNeverSignature *Signature + cachedArgumentsReferenced memoMap[*ast.Node, bool] + enumNumberIndexInfo *IndexInfo + anyBaseTypeIndexInfo *IndexInfo + patternAmbientModules []*ast.PatternAmbientModule + patternAmbientModuleAugmentations ast.SymbolTable + patternAmbientModuleAugmentationTargets ast.SymbolTable + moduleImportAttributesTypes memoMap[*ast.Symbol, *Type] + globalObjectType *Type + globalFunctionType *Type + globalCallableFunctionType *Type + globalNewableFunctionType *Type + globalArrayType *Type + globalReadonlyArrayType *Type + globalStringType *Type + globalNumberType *Type + globalBooleanType *Type + globalRegExpType *Type + globalThisType *Type + anyArrayType *Type + autoArrayType *Type + anyReadonlyArrayType *Type + deferredGlobalImportMetaExpressionType memo[*Type] + emptyStringType *Type + zeroType *Type + zeroBigIntType *Type + typeofType *Type + flowExpressionTypes memoMap[*ast.Node, *Type] + flowLoopCache internMap[FlowLoopKey, *flowLoopCacheEntry] + flowNodeReachable memoMap[*ast.FlowNode, bool] + flowNodePostSuper memoMap[*ast.FlowNode, bool] + subtypeRelation *Relation + strictSubtypeRelation *Relation + assignableRelation *Relation + comparableRelation *Relation + identityRelation *Relation + enumRelation memoMap[EnumRelationKey, RelationComparisonResult] + emitResolverLinks EmitResolverLinks + _jsxNamespace string + _jsxFactoryEntity *ast.Node + packagesMap map[string]bool + ambientModulesOnce sync.Once + ambientModules []*ast.Symbol + reportedUnreachableNodes memoSet[*ast.Node] + unreachableRuns memoMap[*ast.Node, map[*ast.Node]unreachableRun] + checkedRegularExpressions memoSet[*ast.Node] + nonExistentPropertyNodes collections.Set[*ast.Node] // Property names reported as nonexistent + deferredDiagnosticCallbacks []func(c *Checker) + memoStates map[memoKey]*memoState // Memo state of results computed by queries; see memo.go + stateFilter stateKeyFilter // Memo keys that may have a memo state; see memoState + errorFilter errorKeyFilter // Memo keys that may have reports; see observeMemo + memoPendingCount int // Number of memoStates with unreplayed effects + memoErrorStates int // Number of memoStates with reports; see setReported + + mu sync.Mutex } func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { program.BindSourceFiles() - c := &Checker{} + c := &Checker{memoStore: &memoStore{}, checking: true} + c.initResolutions() + c.root = c c.id = nextCheckerID.Add(1) c.tracer = tracer c.program = program c.compilerOptions = program.Options() c.files = program.SourceFiles() c.fileIndexMap = createFileIndexMap(c.files) - c.compareSymbols = c.compareSymbolsWorker // Closure optimization - c.compareSymbolChains = c.compareSymbolChainsWorker // Closure optimization c.languageVersion = c.compilerOptions.GetEmitScriptTarget() c.moduleKind = c.compilerOptions.GetEmitModuleKind() c.moduleResolutionKind = c.compilerOptions.GetModuleResolutionKind() @@ -984,57 +1087,14 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.canCollectSymbolAliasAccessibilityData = c.compilerOptions.VerbatimModuleSyntax.IsFalseOrUnknown() c.arrayVariances = []VarianceFlags{VarianceFlagsCovariant} c.globals = ast.NewSymbolTableWithCapacity(countGlobalSymbols(c.files)) - c.evaluate = evaluator.NewEvaluator(c.evaluateEntity, ast.OEKParentheses) - c.stringLiteralTypes = make(map[string]*Type) - c.numberLiteralTypes = make(map[jsnum.Number]*Type) - c.bigintLiteralTypes = make(map[jsnum.PseudoBigInt]*Type) - c.enumLiteralTypes = make(map[EnumLiteralKey]*Type) - c.enumNaNLiteralTypes = make(map[*ast.Symbol]*Type) - c.indexedAccessTypes = make(map[CacheHashKey]*Type) - c.templateLiteralTypes = make(map[CacheHashKey]*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[CacheHashKey][]*Type) - c.cachedTypes = make(map[CachedTypeKey]*Type) - c.cachedSignatures = make(map[CachedSignatureKey]*Signature) - c.undefinedProperties = make(map[*ast.Symbol]*ast.Symbol) - c.narrowedTypes = make(map[NarrowedTypeKey]*Type) - c.assignmentReducedTypes = make(map[AssignmentReducedKey]*Type) - c.discriminatedContextualTypes = make(map[DiscriminatedContextualTypeKey]*Type) - c.instantiationExpressionTypes = make(map[InstantiationExpressionKey]*Type) - c.substitutionTypes = make(map[SubstitutionTypeKey]*Type) - c.reverseMappedCache = make(map[ReverseMappedTypeKey]*Type) - c.reverseHomomorphicMappedCache = make(map[ReverseMappedTypeKey]*Type) - c.iterationTypesCache = make(map[IterationTypesKey]IterationTypes) c.undefinedSymbol = c.newSymbol(ast.SymbolFlagsProperty, "undefined") c.argumentsSymbol = c.newSymbol(ast.SymbolFlagsProperty, "arguments") 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[CacheHashKey]*Type) - c.moduleSymbols = make(map[*ast.Node]*ast.Symbol) c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) c.globalThisSymbol.SetExports(c.globals, &c.symbolExtraArena) c.globals.Set(c.globalThisSymbol.Name(), c.globalThisSymbol) - c.resolveName = c.createNameResolver().Resolve - c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve - c.tupleTypes = make(map[CacheHashKey]*Type) - c.unionTypes = make(map[CacheHashKey]*Type) - c.objectLiteralTypes = make(map[CacheHashKey]*Type) - c.spreadTypes = make(map[CacheHashKey]*Type) - c.objectContentTypes = make(map[CacheHashKey]*Type) - c.spreadSymbols = make(map[spreadSymbolKey]*ast.Symbol) - c.symbolsWithType = make(map[symbolWithTypeKey]*ast.Symbol) - c.paddedProperties = make(map[symbolWithTypeKey]*ast.Symbol) - c.bindingPatternTypes = make(map[CacheHashKey]*Type) - c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type) - c.intersectionTypes = make(map[CacheHashKey]*Type) - c.intersectionContentTypes = make(map[CacheHashKey]*Type) - c.propertiesTypes = make(map[PropertiesTypesKey]*Type) - c.patternForType = make(map[*Type]*ast.Node) - c.contextFreeTypes = make(map[*ast.Node]*Type) + c.initCheckerState() c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") c.autoType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsNonInferrableType) c.wildcardType = c.newIntrinsicType(TypeFlagsAny, "any") @@ -1076,11 +1136,6 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.numericStringType = c.getTemplateLiteralType([]string{"", ""}, []*Type{c.numberType}) // The `${number}` type c.templateConstraintType = c.getUnionType([]*Type{c.stringType, c.numberType, c.booleanType, c.bigintType, c.nullType, c.undefinedType}) c.uniqueLiteralType = c.newIntrinsicType(TypeFlagsNever, "never") // Special `never` flagged by union reduction to behave as a literal - c.uniqueLiteralMapper = newFunctionTypeMapper(c.getUniqueLiteralTypeForTypeParameter) - c.reportUnreliableMapper = newFunctionTypeMapper(c.reportUnreliableWorker) - c.reportUnmeasurableMapper = newFunctionTypeMapper(c.reportUnmeasurableWorker) - c.restrictiveMapper = newFunctionTypeMapper(c.restrictiveMapperWorker) - c.permissiveMapper = newFunctionTypeMapper(c.permissiveMapperWorker) c.emptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.emptyJsxObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.emptyFreshJsxObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) @@ -1088,7 +1143,6 @@ func NewChecker(program Program, tracer *Tracer) (*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[CacheHashKey]*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) @@ -1096,33 +1150,50 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.resolvingDefaultType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) c.markerSuperType = c.newTypeParameter(nil) c.markerSubType = c.newTypeParameter(nil) - c.markerSubType.AsTypeParameter().constraint = c.markerSuperType + c.markerSubType.AsTypeParameter().constraint.intern(c.markerSuperType) c.markerOtherType = c.newTypeParameter(nil) c.markerSuperTypeForCheck = c.newTypeParameter(nil) c.markerSubTypeForCheck = c.newTypeParameter(nil) - c.markerSubTypeForCheck.AsTypeParameter().constraint = c.markerSuperTypeForCheck + c.markerSubTypeForCheck.AsTypeParameter().constraint.intern(c.markerSuperTypeForCheck) c.noTypePredicate = &TypePredicate{kind: TypePredicateKindIdentifier, parameterIndex: 0, parameterName: "<>", t: c.anyType} c.anySignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0) c.unknownSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.errorType, nil, 0) c.resolvingSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0) c.silentNeverSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.silentNeverType, nil, 0) - c.cachedArgumentsReferenced = make(map[*ast.Node]bool) c.enumNumberIndexInfo = &IndexInfo{keyType: c.numberType, valueType: c.stringType, isReadonly: true} c.anyBaseTypeIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType, isReadonly: false} c.emptyStringType = c.getStringLiteralType("") c.zeroType = c.getNumberLiteralType(0) c.zeroBigIntType = c.getBigIntLiteralType(jsnum.PseudoBigInt{}) c.typeofType = c.getUnionType(core.Map(slices.Sorted(maps.Keys(typeofNEFacts)), c.getStringLiteralType)) - c.flowLoopCache = make(map[FlowLoopKey]*Type) - c.flowNodeReachable = make(map[*ast.FlowNode]bool) - c.flowNodePostSuper = make(map[*ast.FlowNode]bool) c.subtypeRelation = &Relation{} c.strictSubtypeRelation = &Relation{} c.assignableRelation = &Relation{} c.comparableRelation = &Relation{} c.identityRelation = &Relation{} - c.enumRelation = make(map[EnumRelationKey]RelationComparisonResult) - c.moduleImportAttributesTypes = make(map[*ast.Symbol]*Type) + for _, r := range []*Relation{c.subtypeRelation, c.strictSubtypeRelation, c.assignableRelation, c.comparableRelation, c.identityRelation} { + r.results.setKeyBits(relationKeyBits) + } + c.initializeChecker() + c.mergesFrozen = true + c.checking = false + c.syncObserving() + return c, &c.mu +} + +// initCheckerState initializes the per-checker state of c, including closures +// that must be bound to this checker rather than the store. +func (c *Checker) initCheckerState() { + c.compareSymbols = c.compareSymbolsWorker // Closure optimization + c.compareSymbolChains = c.compareSymbolChainsWorker // Closure optimization + c.evaluate = evaluator.NewEvaluator(c.evaluateEntity, ast.OEKParentheses) + c.resolveName = c.createNameResolver().Resolve + c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve + c.uniqueLiteralMapper = newFunctionTypeMapper(c.getUniqueLiteralTypeForTypeParameter) + c.reportUnreliableMapper = newFunctionTypeMapper(c.reportUnreliableWorker) + c.reportUnmeasurableMapper = newFunctionTypeMapper(c.reportUnmeasurableWorker) + c.restrictiveMapper = newFunctionTypeMapper(c.restrictiveMapperWorker) + c.permissiveMapper = newFunctionTypeMapper(c.permissiveMapperWorker) c.getGlobalESSymbolType = c.getGlobalTypeResolver("Symbol", 0 /*arity*/, false /*reportErrors*/) c.getGlobalBigIntType = c.getGlobalTypeResolver("BigInt", 0 /*arity*/, false /*reportErrors*/) c.getGlobalImportMetaType = c.getGlobalTypeResolver("ImportMeta", 0 /*arity*/, true /*reportErrors*/) @@ -1176,8 +1247,24 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.getGlobalClassFieldDecoratorContextType = c.getGlobalTypeResolver("ClassFieldDecoratorContext", 2 /*arity*/, true /*reportErrors*/) c.initializeClosures() c.initializeIterationResolvers() - c.initializeChecker() - return c, &c.mu + // Memos are identified by their addresses, so the cells are allocated + // once all resolvers have their IDs. + if c.globalTypes == nil { + c.globalTypes = make([]memo[resolvedGlobal[*Type]], len(c.globalIDs)) + c.globalSymbols = make([]memo[resolvedGlobal[*ast.Symbol]], len(c.globalIDs)) + } + debug.Assert(len(c.globalTypes) == len(c.globalIDs), "a checker sharing a store resolves a global the store has no cell for") +} + +// Clone returns a new checker sharing c's store. The clone observes and +// contributes to the same memoized state, but has its own resolution stacks. +// Checkers sharing a store must not be used concurrently; acquire the store's +// lock (returned by NewChecker) before use. +func (c *Checker) Clone() *Checker { + clone := &Checker{memoStore: c.memoStore, tracer: c.tracer} + clone.initResolutions() + clone.initCheckerState() + return clone } func createFileIndexMap(files []*ast.SourceFile) map[*ast.SourceFile]int { @@ -1212,39 +1299,102 @@ func (c *Checker) reportUnmeasurableWorker(t *Type) *Type { return t } +// globalKey identifies the resolution of a global by name. Resolutions are +// cached in the store, so all checkers share them. +type globalKey struct { + kind globalKind + name string + arity int + reportErrors bool +} + +// globalID is the small integer standing for a globalKey in the store, so +// that resolving a global hashes an integer rather than a name. Resolvers are +// all created as a checker is initialized, so the first checker of a store +// assigns every ID. +type globalID uint32 + +func (c *Checker) getGlobalID(key globalKey) globalID { + if id, ok := c.globalIDs[key]; ok { + return id + } + if c.globalIDs == nil { + c.globalIDs = make(map[globalKey]globalID) + } + id := globalID(len(c.globalIDs)) + c.globalIDs[key] = id + return id +} + +type globalKind uint8 + +const ( + globalKindType globalKind = iota + globalKindTypeAlias + globalKindValueSymbol + globalKindTypeSymbol +) + // Resolve to the global class or interface by the given name and arity, or emptyObjectType/emptyGenericType otherwise func (c *Checker) getGlobalTypeResolver(name string, arity int, reportErrors bool) func() *Type { - return core.Memoize(func() *Type { - return c.getGlobalType(name, arity, reportErrors) - }) + key := c.getGlobalID(globalKey{globalKindType, name, arity, reportErrors}) + return func() *Type { + m := &c.globalTypes[key] + if r := m.get(c); r.resolved { + return r.value + } + return c.memoCompute(m, func() resolvedGlobal[*Type] { + return resolvedGlobal[*Type]{c.getGlobalType(name, arity, reportErrors), true} + }).value + } +} + +// resolvedGlobal is the result of resolving a global, which may be nil. +type resolvedGlobal[T any] struct { + value T + resolved bool } // Resolve to the global type alias symbol by the given name and arity, or nil otherwise func (c *Checker) getGlobalTypeAliasResolver(name string, arity int, reportErrors bool) func() *ast.Symbol { - return core.Memoize(func() *ast.Symbol { + return c.getGlobalSymbolResolver(globalKey{globalKindTypeAlias, name, arity, reportErrors}, func() *ast.Symbol { return c.getGlobalTypeAliasSymbol(name, arity, reportErrors) }) } // Resolve to the global value symbol by the given name, or nil otherwise func (c *Checker) getGlobalValueSymbolResolver(name string, reportErrors bool) func() *ast.Symbol { - return core.Memoize(func() *ast.Symbol { + return c.getGlobalSymbolResolver(globalKey{globalKindValueSymbol, name, 0, reportErrors}, func() *ast.Symbol { return c.getGlobalSymbol(name, ast.SymbolFlagsValue, core.IfElse(reportErrors, diagnostics.Cannot_find_global_value_0, nil)) }) } func (c *Checker) getGlobalTypeSymbolResolver(name string, reportErrors bool) func() *ast.Symbol { - return core.Memoize(func() *ast.Symbol { + return c.getGlobalSymbolResolver(globalKey{globalKindTypeSymbol, name, 0, reportErrors}, func() *ast.Symbol { return c.getGlobalSymbol(name, ast.SymbolFlagsType, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil)) }) } +func (c *Checker) getGlobalSymbolResolver(k globalKey, resolve func() *ast.Symbol) func() *ast.Symbol { + key := c.getGlobalID(k) + return func() *ast.Symbol { + m := &c.globalSymbols[key] + if r := m.get(c); r.resolved { + return r.value + } + return c.memoCompute(m, func() resolvedGlobal[*ast.Symbol] { + return resolvedGlobal[*ast.Symbol]{resolve(), true} + }).value + } +} + func (c *Checker) getGlobalTypesResolver(names []string, arity int, reportErrors bool) func() []*Type { - return core.Memoize(func() []*Type { - return core.Map(names, func(name string) *Type { - return c.getGlobalType(name, arity, reportErrors) - }) + resolvers := core.Map(names, func(name string) func() *Type { + return c.getGlobalTypeResolver(name, arity, reportErrors) }) + return func() []*Type { + return core.Map(resolvers, func(resolve func() *Type) *Type { return resolve() }) + } } func (c *Checker) getGlobalTypeAliasSymbol(name string, arity int, reportErrors bool) *ast.Symbol { @@ -1254,7 +1404,7 @@ func (c *Checker) getGlobalTypeAliasSymbol(name string, arity int, reportErrors } // Resolve the declared type of the symbol. This resolves type parameters for the type alias so that we can check arity. c.getDeclaredTypeOfSymbol(symbol) - if len(c.typeAliasLinks.Get(symbol).typeParameters) != arity { + if len(c.getTypeParametersOfTypeAlias(symbol)) != arity { if reportErrors { decl := core.Find(symbol.Declarations(), ast.IsTypeAliasDeclaration) c.error(decl, diagnostics.Global_type_0_must_have_1_type_parameter_s, ast.SymbolName(symbol), arity) @@ -1268,6 +1418,12 @@ func (c *Checker) GetTypeAliasTypeParameters(symbol *ast.Symbol) []*Type { if symbol.Flags()&ast.SymbolFlagsTypeAlias == 0 { panic("Attempted to fetch type alias parameters for non-type-alias symbol") } + return c.getTypeParametersOfTypeAlias(symbol) +} + +// getTypeParametersOfTypeAlias returns the type parameters of a type alias, +// which are resolved along with its declared type. +func (c *Checker) getTypeParametersOfTypeAlias(symbol *ast.Symbol) []*Type { c.getDeclaredTypeOfSymbol(symbol) return c.typeAliasLinks.Get(symbol).typeParameters } @@ -1407,10 +1563,10 @@ func (c *Checker) initializeChecker() { } } c.addUndefinedToGlobalsOrErrorOnRedeclaration() - c.valueSymbolLinks.Get(c.undefinedSymbol).resolvedType = c.undefinedWideningType - c.valueSymbolLinks.Get(c.argumentsSymbol).resolvedType = c.getGlobalType("IArguments", 0 /*arity*/, true /*reportErrors*/) - c.valueSymbolLinks.Get(c.unknownSymbol).resolvedType = c.errorType - c.valueSymbolLinks.Get(c.globalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.globalThisSymbol) + c.valueSymbolLinks.Get(c.undefinedSymbol).resolvedType.set(c, c.undefinedWideningType) + c.valueSymbolLinks.Get(c.argumentsSymbol).resolvedType.set(c, c.getGlobalType("IArguments", 0 /*arity*/, true /*reportErrors*/)) + c.valueSymbolLinks.Get(c.unknownSymbol).resolvedType.set(c, c.errorType) + c.valueSymbolLinks.Get(c.globalThisSymbol).resolvedType.set(c, c.newObjectType(ObjectFlagsAnonymous, c.globalThisSymbol)) // Initialize special types c.globalArrayType = c.getGlobalType("Array", 1 /*arity*/, true /*reportErrors*/) c.globalObjectType = c.getGlobalType("Object", 0 /*arity*/, true /*reportErrors*/) @@ -1663,7 +1819,7 @@ func (c *Checker) onFailedToResolveSymbol(errorLocation *ast.Node, name string, // resolution that failed would make the explanation, and any circularity // errors it causes, depend on what else happened to be in progress. Defer // it until no resolution is in progress, so it only observes results. - c.addDeferredDiagnostic(func() { + c.addDeferredDiagnostic(func(c *Checker) { c.reportFailedToResolveSymbol(errorLocation, name, meaning, nameNotFoundMessage) }) } @@ -2262,14 +2418,16 @@ func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *ast.Symbol, meaning ast. func (c *Checker) getImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol { debug.Assert(symbol.Flags()&ast.SymbolFlagsAlias != 0, "Should only get Alias here.") links := c.aliasSymbolLinks.Get(symbol) - if links.immediateTarget == nil { + if links.immediateTarget.get(c) == nil { node := c.getDeclarationOfAliasSymbol(symbol) if node == nil { panic("Unexpected nil in getImmediateAliasedSymbol") } - links.immediateTarget = c.getTargetOfAliasDeclaration(node, true /*dontRecursivelyResolve*/) + links.immediateTarget.begin(c) + links.immediateTarget.set(c, c.getTargetOfAliasDeclaration(node, true /*dontRecursivelyResolve*/)) + c.endMemo() } - return links.immediateTarget + return links.immediateTarget.get(c) } func (c *Checker) addTypeOnlyDeclarationRelatedInfo(diagnostic *ast.Diagnostic, typeOnlyDeclaration *ast.Node, name string) *ast.Diagnostic { @@ -2282,7 +2440,21 @@ func (c *Checker) addTypeOnlyDeclarationRelatedInfo(diagnostic *ast.Diagnostic, func (c *Checker) getSymbol(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { if meaning&ast.SymbolFlagsAll != 0 { - symbol := c.getMergedSymbol(symbols.Get(name)) + symbol := symbols.Get(name) + if symbol == nil { + return nil + } + // Late binding can only merge member declarations into a symbol, so + // skip it when those could not make the symbol match. Resolving the + // late-bound members here would otherwise be circular when looking up + // a name inside one of their computed property names. + if merged := c.mergedSymbolOf(symbol); merged != nil { + symbol = merged + } + if symbol.Flags()&(meaning|ast.SymbolFlagsAlias) == 0 && meaning&(ast.SymbolFlagsClassMember|ast.SymbolFlagsAssignment) == 0 { + return nil + } + symbol = c.getMergedSymbol(symbol) if symbol != nil { if symbol.Flags()&meaning != 0 { return symbol @@ -2301,6 +2473,10 @@ func (c *Checker) getSymbol(symbols ast.SymbolTable, name string, meaning ast.Sy } func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFile, checkUnused bool) { + c.withChecking(func() { c.checkSourceFileWorker(ctx, sourceFile, checkUnused) }) +} + +func (c *Checker) checkSourceFileWorker(ctx context.Context, sourceFile *ast.SourceFile, checkUnused bool) { c.ctx = ctx links := c.sourceFileLinks.Get(sourceFile) if !links.typeChecked { @@ -2319,8 +2495,8 @@ func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFil if !sourceFile.IsDeclarationFile && !c.isCanceled() { c.checkUnusedRenamedBindingElements() } + c.checkExternalHelperRequests(sourceFile) c.produceDeferredDiagnostics() - c.reportedUnreachableNodes.Clear() links.typeChecked = true } if checkUnused && !links.unusedChecked { @@ -2347,12 +2523,16 @@ func (c *Checker) checkSourceElements(nodes []*ast.Node) { func (c *Checker) checkSourceElement(node *ast.Node) bool { if node != nil { - saveCurrentNode := c.currentNode + saveCurrentNode, shared := c.enterSharedCurrentNode(node) + if !shared { + saveCurrentNode = c.enterCurrentNode(node) + } saveWithinUnreachableCode := c.withinUnreachableCode - c.currentNode = node c.instantiationCount = 0 c.checkSourceElementWorker(node) - c.currentNode = saveCurrentNode + if !c.exitSharedCurrentNode(saveCurrentNode) { + c.exitCurrentNode(saveCurrentNode) + } c.withinUnreachableCode = saveWithinUnreachableCode } return false @@ -2497,64 +2677,64 @@ func (c *Checker) checkSourceElementWorker(node *ast.Node) { } func (c *Checker) checkSourceElementUnreachable(node *ast.Node) bool { - if !ast.IsPotentiallyExecutableNode(node) { + if !ast.IsPotentiallyExecutableNode(node) || !c.isSourceElementUnreachable(node) { return false } - - if c.reportedUnreachableNodes.Has(node) { - return true - } - - if !c.isSourceElementUnreachable(node) { - return false - } - - c.reportedUnreachableNodes.Add(node) - - sourceFile := ast.GetSourceFileOfNode(node) - + // Report the whole run of unreachable statements containing node once, at + // the same range whichever statement of the run is checked first. startNode := node endNode := node - - parent := node.Parent - if parent.CanHaveStatements() { - statements := parent.Statements() - if offset := slices.Index(statements, node); offset >= 0 { - // Scan backwards to find the first unreachable unreported node; - // this may happen when producing region diagnostics where not all nodes - // will have been visited. - // TODO: enable this code once we support region diagnostics again. - first := offset - // for i := offset - 1; i >= 0; i-- { - // prevNode := statements[i] - // if !ast.IsPotentiallyExecutableNode(prevNode) || c.reportedUnreachableNodes.Has(prevNode) || !c.isSourceElementUnreachable(prevNode) { - // break - // } - // firstUnreachableIndex = i - // c.reportedUnreachableNodes.Add(prevNode) - // } - - last := offset - for i := offset + 1; i < len(statements); i++ { - nextNode := statements[i] - if !ast.IsPotentiallyExecutableNode(nextNode) || !c.isSourceElementUnreachable(nextNode) { - break - } - last = i - c.reportedUnreachableNodes.Add(nextNode) - } - - startNode = statements[first] - endNode = statements[last] + if parent := node.Parent; parent.CanHaveStatements() { + if run, ok := c.getUnreachableRuns(parent)[node]; ok { + startNode = run.start + endNode = run.end } } + c.reportedUnreachableNodes.once(c, startNode, func() { + sourceFile := ast.GetSourceFileOfNode(node) + start := scanner.GetTokenPosOfNode(startNode, sourceFile, false /*includeJSDoc*/) + diagnostic := ast.NewDiagnostic(sourceFile, core.NewTextRange(start, endNode.End()), diagnostics.Unreachable_code_detected) + c.addErrorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, diagnostic) + }) + return true +} - start := scanner.GetTokenPosOfNode(startNode, sourceFile, false /*includeJSDoc*/) - - diagnostic := ast.NewDiagnostic(sourceFile, core.NewTextRange(start, endNode.End()), diagnostics.Unreachable_code_detected) - c.addErrorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, diagnostic) +type unreachableRun struct { + start *ast.Node + end *ast.Node +} - return true +// getUnreachableRuns returns the run of unreachable statements containing +// each unreachable statement of container. The runs are found together, so +// checking every statement of a long run does not rescan it each time. +func (c *Checker) getUnreachableRuns(container *ast.Node) map[*ast.Node]unreachableRun { + if runs, ok := c.unreachableRuns.get(c, container); ok { + return runs + } + return c.unreachableRuns.compute(c, container, func() map[*ast.Node]unreachableRun { + var runs map[*ast.Node]unreachableRun + isUnreachable := func(n *ast.Node) bool { + return ast.IsPotentiallyExecutableNode(n) && c.isSourceElementUnreachable(n) + } + statements := container.Statements() + for i := 0; i < len(statements); i++ { + if !isUnreachable(statements[i]) { + continue + } + first := i + for i+1 < len(statements) && isUnreachable(statements[i+1]) { + i++ + } + run := unreachableRun{statements[first], statements[i]} + if runs == nil { + runs = make(map[*ast.Node]unreachableRun) + } + for _, statement := range statements[first : i+1] { + runs[statement] = run + } + } + return runs + }) } func (c *Checker) isSourceElementUnreachable(node *ast.Node) bool { @@ -2611,8 +2791,10 @@ func (c *Checker) checkDeferredNode(node *ast.Node) { if tr := c.tracer; tr != nil { defer tr.Push(tracing.PhaseCheck, "checkDeferredNode", map[string]any{"kind": node.Kind, "pos": node.Pos(), "end": node.End(), "path": ast.GetSourceFileOfNode(node).FileName()}, false)() } - saveCurrentNode := c.currentNode - c.currentNode = node + saveCurrentNode, shared := c.enterSharedCurrentNode(node) + if !shared { + saveCurrentNode = c.enterCurrentNode(node) + } c.instantiationCount = 0 switch node.Kind { case ast.KindCallExpression, ast.KindNewExpression, ast.KindTaggedTemplateExpression, ast.KindDecorator, ast.KindJsxOpeningElement: @@ -2642,7 +2824,9 @@ func (c *Checker) checkDeferredNode(node *ast.Node) { case ast.KindObjectLiteralExpression, ast.KindJsxAttributes: c.checkContextualDeprecations(node) } - c.currentNode = saveCurrentNode + if !c.exitSharedCurrentNode(saveCurrentNode) { + c.exitCurrentNode(saveCurrentNode) + } } func (c *Checker) checkJSDocComments(node *ast.Node) { @@ -3071,17 +3255,16 @@ func (c *Checker) checkAccessorDeclaration(node *ast.Node) { symbol := c.getSymbolOfDeclaration(node) getter := ast.GetDeclarationOfKind(symbol, ast.KindGetAccessor) setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor) - if getter != nil && setter != nil && c.nodeLinks.Get(getter).flags&NodeCheckFlagsTypeChecked == 0 { - c.nodeLinks.Get(getter).flags |= NodeCheckFlagsTypeChecked + // Each accessor of the pair reports the errors on its own name, so that + // they do not depend on which of the two is checked first. + if getter != nil && setter != nil && (node == getter || node == setter) { getterFlags := getter.ModifierFlags() setterFlags := setter.ModifierFlags() if (getterFlags & ast.ModifierFlagsAbstract) != (setterFlags & ast.ModifierFlagsAbstract) { - c.error(getter.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract) - c.error(setter.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract) + c.error(node.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract) } if ((getterFlags&ast.ModifierFlagsProtected != 0) && setterFlags&(ast.ModifierFlagsProtected|ast.ModifierFlagsPrivate) == 0) || ((getterFlags&ast.ModifierFlagsPrivate != 0) && setterFlags&ast.ModifierFlagsPrivate == 0) { - c.error(getter.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter) - c.error(setter.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter) + c.error(node.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter) } } } @@ -3244,12 +3427,12 @@ func (c *Checker) checkIfTypePredicateVariableIsDeclaredInBindingPattern(pattern } func (c *Checker) checkTypeQuery(node *ast.Node) { - c.getTypeFromTypeQueryNode(node) + memoTypeNode(c, node, (*Checker).getTypeFromTypeQueryNode) } func (c *Checker) checkTypeLiteral(node *ast.Node) { c.checkSourceElements(node.Members()) - t := c.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node) + t := memoTypeNode(c, node, (*Checker).getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode) c.checkIndexConstraints(t, t.symbol, false /*isStaticIndex*/) c.checkTypeForDuplicateIndexSignatures(node) c.checkObjectTypeForDuplicateDeclarations(node, false /*checkPrivateNames*/) @@ -3389,7 +3572,7 @@ func (c *Checker) checkUnionOrIntersectionType(node *ast.Node) { } func (c *Checker) checkThisType(node *ast.Node) { - c.getTypeFromThisTypeNode(node) + memoTypeNode(c, node, (*Checker).getTypeFromThisTypeNode) } func (c *Checker) checkTypeOperator(node *ast.Node) { @@ -3411,9 +3594,7 @@ func (c *Checker) checkInferType(node *ast.Node) { c.checkSourceElement(typeParameterDeclarationNode) symbol := c.getSymbolOfDeclaration(typeParameterDeclarationNode) if len(symbol.Declarations()) > 1 { - links := c.declaredTypeLinks.Get(symbol) - if !links.typeParametersChecked { - links.typeParametersChecked = true + c.typeParametersChecked.once(c, symbol, func() { typeParameter := c.getDeclaredTypeOfTypeParameter(symbol) declarations := getDeclarationsOfKind(symbol, ast.KindTypeParameter) if !c.areTypeParametersIdentical(declarations, []*Type{typeParameter}, func(decl *ast.Node) []*ast.Node { return []*ast.Node{decl} }) { @@ -3423,7 +3604,7 @@ func (c *Checker) checkInferType(node *ast.Node) { c.error(declaration.Name(), diagnostics.All_declarations_of_0_must_have_identical_constraints, name) } } - } + }) } c.registerForUnusedIdentifiersCheck(node) } @@ -3474,7 +3655,7 @@ func (c *Checker) checkNamedTupleMember(node *ast.Node) { func (c *Checker) checkIndexedAccessType(node *ast.Node) { node.ForEachChild(c.checkSourceElement) - c.checkIndexedAccessIndexType(c.getTypeFromIndexedAccessTypeNode(node), node) + c.checkIndexedAccessIndexType(memoTypeNode(c, node, (*Checker).getTypeFromIndexedAccessTypeNode), node) } func (c *Checker) checkMappedType(node *ast.Node) { @@ -3486,7 +3667,7 @@ func (c *Checker) checkMappedType(node *ast.Node) { if mappedTypeNode.Type == nil { c.reportImplicitAny(node, c.anyType, WideningKindNormal) } - t := c.getTypeFromMappedTypeNode(node) + t := memoTypeNode(c, node, (*Checker).getTypeFromMappedTypeNode) nameType := c.getNameTypeFromMappedType(t) if nameType != nil { c.checkTypeAssignableTo(nameType, c.stringNumberSymbolType, mappedTypeNode.NameType, nil) @@ -3555,10 +3736,7 @@ func (c *Checker) checkFunctionOrMethodDeclaration(node *ast.Node) { } func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { - // Only check the symbol once - if c.constructorOrFunctionChecked.AddIfAbsent(symbol) { - c.checkFunctionOrConstructorSymbolWorker(symbol) - } + c.constructorOrFunctionChecked.once(c, symbol, func() { c.checkFunctionOrConstructorSymbolWorker(symbol) }) } func (c *Checker) checkFunctionOrConstructorSymbolWorker(symbol *ast.Symbol) { @@ -4526,9 +4704,7 @@ func (c *Checker) checkTypeParameterListsIdentical(symbol *ast.Symbol) { if len(symbol.Declarations()) == 1 { return } - links := c.declaredTypeLinks.Get(symbol) - if !links.typeParametersChecked { - links.typeParametersChecked = true + c.typeParametersChecked.once(c, symbol, func() { declarations := c.getClassOrInterfaceDeclarationsOfSymbol(symbol) if len(declarations) <= 1 { return @@ -4541,7 +4717,7 @@ func (c *Checker) checkTypeParameterListsIdentical(symbol *ast.Symbol) { c.error(declaration.Name(), diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name) } } - } + }) } func (c *Checker) getClassOrInterfaceDeclarationsOfSymbol(symbol *ast.Symbol) []*ast.Node { @@ -4627,8 +4803,8 @@ func (c *Checker) getTypeWithoutSignatures(t *Type) *Type { if len(resolved.getSignatures()) != 0 { result := c.newObjectType(ObjectFlagsAnonymous, t.symbol) result.objectFlags |= ObjectFlagsMembersResolved - result.AsObjectType().members = resolved.getMembers() - result.AsObjectType().properties = resolved.getProperties() + result.AsObjectType().members = resolved.getMembers(c) + result.AsObjectType().properties = resolved.getProperties(c) return result } case t.flags&TypeFlagsIntersection != 0: @@ -5038,10 +5214,9 @@ func (c *Checker) checkIndexConstraintForIndexSignature(t *Type, checkInfo *Inde func (c *Checker) checkClassOrInterfaceForDuplicateIndexSignatures(node *ast.Node) { // Only check the type once - if links := c.declaredTypeLinks.Get(c.getSymbolOfDeclaration(node)); !links.indexSignaturesChecked { - links.indexSignaturesChecked = true + c.indexSignaturesChecked.once(c, c.getSymbolOfDeclaration(node), func() { c.checkTypeForDuplicateIndexSignatures(node) - } + }) } func (c *Checker) checkTypeForDuplicateIndexSignatures(node *ast.Node) { @@ -5145,8 +5320,7 @@ func (c *Checker) checkInterfaceDeclaration(node *ast.Node) { c.checkTypeParameterListsIdentical(symbol) // Check once per checker, but report on the first interface declaration, // independently of which declaration is checked first. - if links := c.declaredTypeLinks.Get(symbol); !links.interfaceChecked { - links.interfaceChecked = true + c.interfaceChecked.once(c, symbol, func() { firstInterfaceDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindInterfaceDeclaration) t := c.getDeclaredTypeOfSymbol(symbol) typeWithThis := c.getTypeWithThisArgument(t, nil, false) @@ -5157,7 +5331,8 @@ func (c *Checker) checkInterfaceDeclaration(node *ast.Node) { } c.checkIndexConstraints(t, symbol /*isStaticIndex*/, false) } - } + }) + c.checkObjectTypeForDuplicateDeclarations(node, false /*checkPrivateNames*/) for _, heritageElement := range ast.GetExtendsHeritageClauseElements(node) { if ast.IsExpressionWithTypeArguments(heritageElement) { @@ -5232,8 +5407,7 @@ func (c *Checker) checkEnumDeclaration(node *ast.Node) { // // Only perform this check once per symbol enumSymbol := c.getSymbolOfDeclaration(node) - if links := c.declaredTypeLinks.Get(enumSymbol); !links.enumChecked { - links.enumChecked = true + c.enumChecked.once(c, enumSymbol, func() { if len(enumSymbol.Declarations()) > 1 { firstEnumDeclaration := ast.GetDeclarationOfKind(enumSymbol, ast.KindEnumDeclaration) enumIsConst := ast.IsEnumConst(firstEnumDeclaration) @@ -5263,7 +5437,7 @@ func (c *Checker) checkEnumDeclaration(node *ast.Node) { } } } - } + }) } func (c *Checker) checkEnumMember(node *ast.Node) { @@ -5396,9 +5570,10 @@ func (c *Checker) getTypeOfModuleDeclarationImportAttributes(attributes *ast.Typ } func (c *Checker) getTypeOfModuleImportAttributes(symbol *ast.Symbol) *Type { - if t, ok := c.moduleImportAttributesTypes[symbol]; ok { + if t, ok := c.moduleImportAttributesTypes.get(c, symbol); ok { return t } + c.moduleImportAttributesTypes.begin(c, symbol) var result *Type moduleDecl := core.Find(symbol.Declarations(), ast.IsModuleWithStringLiteralName) if moduleDecl == nil { @@ -5406,7 +5581,8 @@ func (c *Checker) getTypeOfModuleImportAttributes(symbol *ast.Symbol) *Type { } else { result = c.getTypeOfModuleDeclarationImportAttributes(moduleDecl.AsModuleDeclaration().Attributes) } - c.moduleImportAttributesTypes[symbol] = result + c.moduleImportAttributesTypes.set(c, symbol, result) + c.endMemo() return result } @@ -5639,19 +5815,19 @@ func (c *Checker) getTypeFromImportAttributes(node *ast.Node) *Type { func (c *Checker) checkImportAttributesExpression(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { symbol := c.newSymbol(ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameImportAttributes) members := ast.NewSymbolTable() for _, attribute := range node.AsImportAttributes().Attributes.Nodes { member := c.newSymbol(ast.SymbolFlagsProperty, attribute.Name().Text()) - c.valueSymbolLinks.Get(member).resolvedType = c.getRegularTypeOfLiteralType(c.checkExpressionCached(attribute.AsImportAttribute().Value)) + c.valueSymbolLinks.Get(member).resolvedType.setIdentity(c, c.getRegularTypeOfLiteralType(c.checkExpressionCached(attribute.AsImportAttribute().Value))) members.Set(member.Name(), member) } t := c.newAnonymousType(symbol, members, nil, nil, nil) t.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsNonInferrableType - links.resolvedType = t + links.resolvedType.set(c, t) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getImportAttributesTypeForModuleSpecifier(moduleSpecifier *ast.Node) *Type { @@ -5918,8 +6094,7 @@ func getVerbatimModuleSyntaxErrorMessage(node *ast.Node) *diagnostics.Message { func (c *Checker) checkExternalModuleExports(node *ast.Node) { moduleSymbol := c.getSymbolOfDeclaration(node) - links := c.moduleSymbolLinks.Get(moduleSymbol) - if !links.exportsChecked { + c.moduleExportsChecked.once(c, moduleSymbol, func() { exportEqualsSymbol := moduleSymbol.Exports().Get(ast.InternalSymbolNameExportEquals) // An export assignment is in error if (a) the module exports value members or (b) if the module exports type or // namespace members and the exported entity also exports type or namespace members. @@ -5957,8 +6132,7 @@ func (c *Checker) checkExternalModuleExports(node *ast.Node) { } } } - links.exportsChecked = true - } + }) } func (c *Checker) hasExportedMembersOfKind(moduleSymbol *ast.Symbol, kind ast.SymbolFlags) bool { @@ -6046,6 +6220,10 @@ func (c *Checker) checkVariableLikeDeclaration(node *ast.Node) { // ^^^^^^ // variable renaming in function type notation is confusing, // so we forbid it even if noUnusedLocals is not enabled + if !c.checking { + c.recordEffect(func(c *Checker) { c.renamedBindingElementsInTypes = append(c.renamedBindingElementsInTypes, node) }) + return + } c.renamedBindingElementsInTypes = append(c.renamedBindingElementsInTypes, node) // Still resolve the type like the end of this function does for every other declaration, // so implicit any errors are reported by checking instead of by whatever asks for the type later @@ -6266,6 +6444,13 @@ func (c *Checker) checkDecorators(node *ast.Node) { // skip this check for nodes that cannot have decorators. These should have already had an error reported by // checkGrammarModifiers. if !ast.CanHaveDecorators(node) || !ast.HasDecorators(node) || !ast.NodeCanBeDecorated(c.legacyDecorators, node, node.Parent, node.Parent.Parent) { + // The expressions of decorators that are not valid here are still + // checked, as they are when anything computes their types. + for _, modifier := range node.ModifierNodes() { + if ast.IsDecorator(modifier) { + c.checkExpression(modifier.Expression()) + } + } return } firstDecorator := core.Find(node.ModifierNodes(), ast.IsDecorator) @@ -6311,11 +6496,11 @@ func (c *Checker) checkDecorator(node *ast.Node) { } // if we fail to get a signature and return type here, we will have already reported a grammar error in `checkDecorators`. decoratorSignature := c.getDecoratorCallSignature(node) - if decoratorSignature == nil || decoratorSignature.resolvedReturnType == nil { + if decoratorSignature == nil || decoratorSignature.resolvedReturnType.get(c) == nil { return } var headMessage *diagnostics.Message - expectedReturnType := decoratorSignature.resolvedReturnType + expectedReturnType := decoratorSignature.resolvedReturnType.get(c) switch node.Parent.Kind { case ast.KindClassDeclaration, ast.KindClassExpression: headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1 @@ -6510,20 +6695,24 @@ func (c *Checker) getIterationTypesOfIterable(t *Type, use IterationUse, errorNo if IsTypeAny(t) { return IterationTypes{c.anyType, c.anyType, c.anyType} } - key := IterationTypesKey{typeId: t.id, use: use & IterationUseCacheFlags} - // If we are reporting errors and encounter a cached `noIterationTypes`, we should ignore the cached value and continue as if nothing was cached. - // In addition, we should not cache any new results for this call. - noCache := false - if cached, ok := c.iterationTypesCache[key]; ok { - if errorNode == nil || cached.hasTypes() { - return cached - } - noCache = true + if errorNode != nil { + // Errors belong to errorNode, so compute them here rather than + // cache them with the types every other use of t shares. + return c.getIterationTypesOfIterableWorker(t, use, errorNode) } - result := c.getIterationTypesOfIterableWorker(t, use, errorNode) - if !noCache { - c.iterationTypesCache[key] = result + key := IterationTypesKey{typeId: t.id, use: use & IterationUseCacheFlags} + c.iterationTypesCache.begin(c, key) + result := c.getIterationTypesOfIterableCached(t, use, key) + c.endMemo() + return result +} + +func (c *Checker) getIterationTypesOfIterableCached(t *Type, use IterationUse, key IterationTypesKey) IterationTypes { + if cached, ok := c.iterationTypesCache.get(c, key); ok { + return cached } + result := c.getIterationTypesOfIterableWorker(t, use, nil /*errorNode*/) + c.iterationTypesCache.set(c, key, result) return result } @@ -6534,7 +6723,7 @@ func (c *Checker) getIterationTypesOfIterableWorker(t *Type, use IterationUse, e iterationTypes := c.getIterationTypesOfIterableWorker(constituent, use, nil) if !iterationTypes.hasTypes() { if errorNode != nil { - c.addDeferredDiagnostic(func() { + c.addDeferredDiagnostic(func(c *Checker) { c.reportTypeNotIterableError(errorNode, t, use&IterationUseAllowsAsyncIterablesFlag != 0) }) } @@ -6587,7 +6776,7 @@ func (c *Checker) getIterationTypesOfIterableWorker(t *Type, use IterationUse, e if errorNode != nil { // We defer the diagnostic because TypeToString may attempt to resolve symbols that are already being // resolved, possibly causing circularities. - c.addDeferredDiagnostic(func() { + c.addDeferredDiagnostic(func(c *Checker) { diagnostic := c.reportTypeNotIterableError(errorNode, t, use&IterationUseAllowsAsyncIterablesFlag != 0) for _, d := range diags { diagnostic.AddRelatedInfo(d) @@ -7164,9 +7353,13 @@ func (c *Checker) checkExportsOnMergedDeclarations(node *ast.Node) { return } } - if len(symbol.Declarations()) < 2 || !c.mergedExportsChecked.AddIfAbsent(symbol) { + if len(symbol.Declarations()) < 2 { return } + c.mergedExportsChecked.once(c, symbol, func() { c.checkExportsOnMergedDeclarationsWorker(symbol) }) +} + +func (c *Checker) checkExportsOnMergedDeclarationsWorker(symbol *ast.Symbol) { exportedDeclarationSpaces := DeclarationSpacesNone nonExportedDeclarationSpaces := DeclarationSpacesNone defaultExportedDeclarationSpaces := DeclarationSpacesNone @@ -7584,18 +7777,44 @@ func (c *Checker) getTypeOfExpression(node *ast.Node) *Type { if quickType != nil { return quickType } - // If a type has been cached for the node, return it. + if len(c.flowLoopStack) == 0 { + // Outside loop analysis, the type is independent of the flow state in + // progress, so it is cached in the store. + if t, ok := c.flowExpressionTypes.get(c, node); ok { + return t + } + c.flowExpressionTypes.begin(c, node) + startInvocationCount := c.flowInvocationCount + t := c.checkExpressionEx(node, CheckModeTypeOnly) + // If control flow analysis was required to determine the type, it is worth caching. + if c.flowInvocationCount != startInvocationCount { + c.flowExpressionTypes.set(c, node, t) + c.endMemo() + } else { + c.endMemoUncached() + } + return t + } + // Within loop analysis, the type may depend on the transient types of the + // loop, so it is only cached until the analysis of the loop is restarted. + key := memoKeyOf(node, memoFlowExpressionType) if cachedType := c.flowTypeCache[node]; cachedType != nil { + if c.memoActive() { + c.observeMemo(key) + } return cachedType } + c.beginMemo(key) startInvocationCount := c.flowInvocationCount t := c.checkExpressionEx(node, CheckModeTypeOnly) - // If control flow analysis was required to determine the type, it is worth caching. if c.flowInvocationCount != startInvocationCount { if c.flowTypeCache == nil { c.flowTypeCache = make(map[*ast.Node]*Type) } c.flowTypeCache[node] = t + c.endMemo() + } else { + c.endMemoUncached() } return t } @@ -7763,7 +7982,21 @@ func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) * return c.checkExpressionEx(node, checkMode) } links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if t := links.resolvedType.get(c); t != nil { + return t + } + links.resolvedType.begin(c) + result := c.checkExpressionCachedExUncached(node, checkMode) + c.endMemo() + return result +} + +func (c *Checker) checkExpressionCachedExUncached(node *ast.Node, checkMode CheckMode) *Type { + if checkMode != CheckModeNormal { + return c.checkExpressionEx(node, checkMode) + } + links := c.typeNodeLinks.Get(node) + if links.resolvedType.get(c) == nil { // When computing a type that we're going to cache, we need to ignore any ongoing control flow // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart // to the top of the stack ensures all transient types are computed from a known point. @@ -7771,11 +8004,11 @@ func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) * saveFlowTypeCache := c.flowTypeCache c.flowLoopStack = nil c.flowTypeCache = nil - links.resolvedType = c.checkExpressionEx(node, checkMode) + links.resolvedType.set(c, c.checkExpressionEx(node, checkMode)) c.flowTypeCache = saveFlowTypeCache c.flowLoopStack = saveFlowLoopStack } - return links.resolvedType + return links.resolvedType.get(c) } // Returns the type of an expression. Unlike checkExpression, this function is simply concerned @@ -7784,12 +8017,14 @@ func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) * // and requesting the contextual type might cause a circularity or other bad behaviour. // It sets the contextual type of the node to any before calling getTypeOfExpression. func (c *Checker) getContextFreeTypeOfExpression(node *ast.Node) *Type { - if cached := c.contextFreeTypes[node]; cached != nil { + if cached, _ := c.contextFreeTypes.get(c, node); cached != nil { return cached } + c.contextFreeTypes.begin(c, node) c.pushContextualType(node, c.anyType, false /*isCache*/) t := c.checkExpressionEx(node, CheckModeSkipContextSensitive) - c.contextFreeTypes[node] = t + c.contextFreeTypes.set(c, node, t) + c.endMemo() c.popContextualType() return t } @@ -7802,15 +8037,19 @@ func (c *Checker) checkExpressionEx(node *ast.Node, checkMode CheckMode) *Type { if tr := c.tracer; tr != nil { defer tr.Push(tracing.PhaseCheck, "checkExpression", map[string]any{"kind": node.Kind, "pos": node.Pos(), "end": node.End(), "path": ast.GetSourceFileOfNode(node).FileName()}, false)() } - saveCurrentNode := c.currentNode - c.currentNode = node + saveCurrentNode, shared := c.enterSharedCurrentNode(node) + if !shared { + saveCurrentNode = c.enterCurrentNode(node) + } c.instantiationCount = 0 uninstantiatedType := c.checkExpressionWorker(node, checkMode) t := c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode) if isConstEnumObjectType(t) { c.checkConstEnumAccess(node, t) } - c.currentNode = saveCurrentNode + if !c.exitSharedCurrentNode(saveCurrentNode) { + c.exitCurrentNode(saveCurrentNode) + } return t } @@ -8089,10 +8328,10 @@ func (c *Checker) checkPrivateIdentifierExpression(node *ast.Node) *Type { func (c *Checker) getSymbolForPrivateIdentifierExpression(node *ast.Node) *ast.Symbol { links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol == nil { - links.resolvedSymbol = c.lookupSymbolForPrivateIdentifierDeclaration(node.Text(), node) + if links.resolvedSymbol.get(c) == nil { + links.resolvedSymbol.set(c, c.lookupSymbolForPrivateIdentifierDeclaration(node.Text(), node)) } - return links.resolvedSymbol + return links.resolvedSymbol.get(c) } func (c *Checker) checkSuperExpression(node *ast.Node) *Type { @@ -8254,11 +8493,9 @@ func (c *Checker) isTemplateLiteralContextualType(t *Type) bool { } func (c *Checker) checkRegularExpressionLiteral(node *ast.Node) *Type { - nodeLinks := c.nodeLinks.Get(node) - if nodeLinks.flags&NodeCheckFlagsTypeChecked == 0 { - nodeLinks.flags |= NodeCheckFlagsTypeChecked + c.checkedRegularExpressions.once(c, node, func() { c.checkGrammarRegularExpressionLiteral(node.AsRegularExpressionLiteral()) - } + }) return c.globalRegExpType } @@ -8271,7 +8508,7 @@ func (c *Checker) checkArrayLiteral(node *ast.Node, checkMode CheckMode) *Type { inConstContext := c.isConstContext(node) contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNone) inTupleContext := isSpreadIntoCallOrNew(node) || contextualType != nil && someType(contextualType, func(t *Type) bool { - return c.isTupleLikeType(t) || c.isGenericMappedType(t) && t.AsMappedType().nameType == nil && c.getHomomorphicTypeVariable(core.OrElse(t.AsMappedType().target, t)) != nil + return c.isTupleLikeType(t) || c.isGenericMappedType(t) && c.getNameTypeFromMappedType(t) == nil && c.getHomomorphicTypeVariable(core.OrElse(t.AsMappedType().target, t)) != nil }) hasOmittedExpression := false for i, e := range elements { @@ -8349,12 +8586,12 @@ func (c *Checker) createArrayLiteralType(t *Type) *Type { return t } key := CachedTypeKey{kind: CachedTypeKindArrayLiteralType, typeId: t.id} - if cached, ok := c.cachedTypes[key]; ok { + if cached, ok := c.cachedTypes.get(c, key); ok { return cached } literalType := c.cloneTypeReference(t) literalType.objectFlags |= ObjectFlagsArrayLiteral | ObjectFlagsContainsObjectOrArrayLiteral - c.cachedTypes[key] = literalType + c.cachedTypes.intern(key, literalType) return literalType } @@ -8723,20 +8960,29 @@ func (c *Checker) getResolvedSignature(node *ast.Node, candidatesOutArray *[]*Si // Resolution is located at the call however it was requested, such as // from contextually typing an argument, so that errors it reports at the // current node do not depend on which was checked first. - saveCurrentNode := c.currentNode - c.currentNode = node - result := c.resolveSignatureOfCall(node, candidatesOutArray, checkMode) - c.currentNode = saveCurrentNode + saveCurrentNode, shared := c.enterSharedCurrentNode(node) + if !shared { + saveCurrentNode = c.enterCurrentNode(node) + } + c.signatureLinks.Get(node).resolvedSignature.begin(c) + result := c.getResolvedSignatureUncached(node, candidatesOutArray, checkMode) + c.endMemo() + if !c.exitSharedCurrentNode(saveCurrentNode) { + c.exitCurrentNode(saveCurrentNode) + } return result } -func (c *Checker) resolveSignatureOfCall(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { +func (c *Checker) getResolvedSignatureUncached(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { links := c.signatureLinks.Get(node) // If getResolvedSignature has already been called, we will have cached the resolvedSignature. // However, it is possible that either candidatesOutArray was not passed in the first time, // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work // to correctly fill the candidatesOutArray. - cached := links.resolvedSignature + cached := links.resolvedSignature.get(c) + if cached == c.resolvingSignature && c.isSignatureResolutionHidden(links) { + cached = nil + } if cached != nil && cached != c.resolvingSignature && candidatesOutArray == nil { return cached } @@ -8749,7 +8995,10 @@ func (c *Checker) resolveSignatureOfCall(node *ast.Node, candidatesOutArray *[]* if (ast.IsCallExpression(node) || ast.IsNewExpression(node) || ast.IsTaggedTemplateExpression(node) || ast.IsJsxOpeningLikeElement(node)) && isContextuallyTypedByEnclosingCall(node) { c.getContextualType(node, ContextFlagsNone) } - cached = links.resolvedSignature + cached = links.resolvedSignature.get(c) + if cached == c.resolvingSignature && c.isSignatureResolutionHidden(links) { + cached = nil + } if cached != nil && cached != c.resolvingSignature && candidatesOutArray == nil { return cached } @@ -8762,10 +9011,12 @@ func (c *Checker) resolveSignatureOfCall(node *ast.Node, candidatesOutArray *[]* // through resolution of the containing function call. By resetting the resolution stack we'll // retry the symbol type resolution with the resolvingSignature marker in place to suppress // the contextual type circularity. - c.resolutionStart = len(c.typeResolutions) + c.resolutionStart = len(c.resolutions) } - links.resolvedSignature = c.resolvingSignature + links.resolvedSignature.swap(c.resolvingSignature) + c.signaturesResolving = append(c.signaturesResolving, links) result := c.resolveSignature(node, candidatesOutArray, checkMode) + c.signaturesResolving = c.signaturesResolving[:len(c.signaturesResolving)-1] c.resolutionStart = saveResolutionStart // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call // resolution should be deferred. @@ -8775,21 +9026,39 @@ func (c *Checker) resolveSignatureOfCall(node *ast.Node, candidatesOutArray *[]* // since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature // it's possible that this inner resolution sets the resolvedSignature first. // In such a case we ignore the local result and reuse the correct one that was cached. - if links.resolvedSignature != c.resolvingSignature { - result = links.resolvedSignature + if links.resolvedSignature.get(c) != c.resolvingSignature { + result = links.resolvedSignature.get(c) } // If signature resolution originated in control flow type analysis (for example to compute the // assigned type in a flow assignment) we don't cache the result as it may be based on temporary // types from the control flow analysis. if len(c.flowLoopStack) == 0 { - links.resolvedSignature = result + c.setResolvedSignature(links, result) } else { - links.resolvedSignature = cached + links.resolvedSignature.swap(cached) } } return result } +// setResolvedSignature caches the signature a call resolves to, in place of +// the marker of its resolution, which is only restored while the resolution +// is still in progress. +func (c *Checker) setResolvedSignature(links *SignatureLinks, signature *Signature) { + setAfterMarker(c, &links.resolvedSignature, signature, links, isStaleSignatureMarker) +} + +func isStaleSignatureMarker(c *Checker, links *SignatureLinks, s *Signature) bool { + return s == c.resolvingSignature && !slices.Contains(c.signaturesResolving, links) +} + +// isSignatureResolutionHidden reports whether the resolution of the call +// with the given links is in progress below what is visible; see yieldCycle. +func (c *Checker) isSignatureResolutionHidden(links *SignatureLinks) bool { + i := slices.Index(c.signaturesResolving[c.visibleSignatures:], links) + return i < 0 && c.visibleSignatures > 0 && slices.Contains(c.signaturesResolving[:c.visibleSignatures], links) +} + func (c *Checker) resolveSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { switch node.Kind { case ast.KindCallExpression: @@ -9271,15 +9540,18 @@ func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidate // is just important for choosing the best signature. So in the case where there is only one // signature, the subtype pass is useless. So skipping it is an optimization. var result *Signature - s.recursiveResolution = slices.Contains(c.callResolutionStack, s.node) - c.callResolutionStack = append(c.callResolutionStack, s.node) + if i := c.resolvingAnywhereIndex(s.node, TypeSystemPropertyNameCallResolution); i >= 0 && !c.yieldCycle(i) { + c.markTypeResolutionCircular(i) + s.recursiveResolution = true + } + c.beginTypeResolution(s.node, TypeSystemPropertyNameCallResolution, nil) if len(s.candidates) > 1 { result = c.chooseOverload(&s, c.subtypeRelation) } if result == nil { result = c.chooseOverload(&s, c.assignableRelation) } - c.callResolutionStack = c.callResolutionStack[:len(c.callResolutionStack)-1] + c.popTypeResolution() if result != nil { return result } @@ -9291,7 +9563,7 @@ func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidate // don't hit this issue because they only observe this result after it's had a chance to // be cached, but the error reporting code below executes before getResolvedSignature sets // resolvedSignature. - c.signatureLinks.Get(node).resolvedSignature = result + c.signatureLinks.Get(node).resolvedSignature.set(c, result) // No signatures were applicable. Now report errors based on the last applicable signature with // no arguments excluded from assignability checks. // If candidate is undefined, it means that no candidates had a suitable arity. In that case, @@ -9367,12 +9639,14 @@ func (c *Checker) getOptionalCallSignature(signature *Signature, callChainFlags return signature } key := CachedSignatureKey{sig: signature, key: core.IfElse(callChainFlags == SignatureFlagsIsInnerCallChain, SignatureKeyInner, SignatureKeyOuter)} - if cached := c.cachedSignatures[key]; cached != nil { + if cached, _ := c.cachedSignatures.get(c, key); cached != nil { return cached } + c.cachedSignatures.begin(c, key) result := c.cloneSignature(signature) result.flags |= callChainFlags - c.cachedSignatures[key] = result + c.cachedSignatures.set(c, key, result) + c.endMemo() return result } @@ -10485,14 +10759,16 @@ func (c *Checker) checkFunctionExpressionOrObjectLiteralMethod(node *ast.Node, c // Return plain anyFunctionType if there is no possibility we'll make inferences from the return type contextualSignature := c.getContextualSignature(node) if contextualSignature != nil && c.couldContainTypeVariables(c.getReturnTypeOfSignature(contextualSignature)) { - if cached, ok := c.contextFreeTypes[node]; ok { + if cached, ok := c.contextFreeTypes.get(c, node); ok { return cached } + c.contextFreeTypes.begin(c, node) returnType := c.getReturnTypeFromBody(node, checkMode) returnOnlySignature := c.newSignature(SignatureFlagsIsNonInferrable, nil, nil /*typeParameters*/, nil /*thisParameter*/, nil, returnType, nil /*resolvedTypePredicate*/, 0) returnOnlyType := c.newAnonymousType(node.Symbol(), nil, []*Signature{returnOnlySignature}, nil, nil) returnOnlyType.objectFlags |= ObjectFlagsNonInferrableType - c.contextFreeTypes[node] = returnOnlyType + c.contextFreeTypes.set(c, node, returnOnlyType) + c.endMemo() return returnOnlyType } } @@ -10514,19 +10790,45 @@ func (c *Checker) checkFunctionExpressionOrObjectLiteralMethod(node *ast.Node, c func (c *Checker) contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node *ast.Node, checkMode CheckMode) { c.assignContextualParameterTypesOfFunction(node, checkMode) + key := memoKeyOf(node, memoContextChecked) + // A check in progress below a cycle yield is hidden, so the function is + // checked again as if it were not, as computing from scratch would. + i, ok := c.resolving.get(key) + hidden := ok && int(i) < c.visibleStart + c.beginMemo(key) + c.contextuallyCheckFunctionExpressionOrObjectLiteralMethodUncached(node, checkMode, hidden) + c.endMemo() +} + +func (c *Checker) contextuallyCheckFunctionExpressionOrObjectLiteralMethodUncached(node *ast.Node, checkMode CheckMode, hidden bool) { links := c.nodeLinks.Get(node) - if links.flags&NodeCheckFlagsContextChecked == 0 || links.flags&NodeCheckFlagsSignatureContextChecked != 0 { + if links.flags&NodeCheckFlagsContextChecked == 0 || links.flags&NodeCheckFlagsSignatureContextChecked != 0 && !hidden { return } - links.flags |= NodeCheckFlagsSignatureContextChecked + // The flag guards against checking the function again from within the + // check, so it is set now, and is only discarded if a cycle in progress + // logs it. + markerLogged := c.logResults() + setFlagMemoBits(c, memoKeyOf(node, memoContextChecked), &links.flags, NodeCheckFlagsSignatureContextChecked, NodeCheckFlagsSignatureContextChecked) signature := c.getSignatureFromDeclarationUncontextual(node) contextualSignature := c.getContextualSignature(node) - if contextualSignature != nil && c.getReturnTypeFromAnnotation(node) == nil && signature.resolvedReturnType == nil { + // The return type is inferred now, while the contextual types of the + // function's enclosing expressions are in place; a later request may + // come from where they are not. + if contextualSignature != nil && c.getReturnTypeFromAnnotation(node) == nil && signature.resolvedReturnType.get(c) == nil && !c.isResolvingReturnTypeOfSignature(signature) { // resolvedReturnType is cached indefinitely, so the return type here has to be computed without CheckModeSkipContextSensitive; // otherwise anyFunctionType could leak as part of the computed (and cached) return type. returnType := c.getReturnTypeFromBody(node, checkMode&^CheckModeSkipContextSensitive) - if signature.resolvedReturnType == nil { - signature.resolvedReturnType = returnType + if signature.resolvedReturnType.get(c) == nil { + if markerLogged { + signature.resolvedReturnType.set(c, returnType) + } else { + // A cycle detected since the flag was set would discard the + // return type but keep the flag, so a later request would + // infer the return type without the contextual types in + // place now. Keep the return type with the flag instead. + signature.resolvedReturnType.setIdentity(c, returnType) + } } } c.checkSignatureDeclaration(node) @@ -10538,6 +10840,12 @@ func (c *Checker) contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node // signature of the function is not complete until then, so // getSignatureFromDeclaration requests this before returning it. func (c *Checker) assignContextualParameterTypesOfFunction(node *ast.Node, checkMode CheckMode) { + c.beginMemo(memoKeyOf(node, memoContextualParameters)) + c.assignContextualParameterTypesOfFunctionUncached(node, checkMode) + c.endMemo() +} + +func (c *Checker) assignContextualParameterTypesOfFunctionUncached(node *ast.Node, checkMode CheckMode) { links := c.nodeLinks.Get(node) if links.flags&NodeCheckFlagsContextChecked == 0 { contextualSignature := c.getContextualSignature(node) @@ -10545,8 +10853,14 @@ func (c *Checker) assignContextualParameterTypesOfFunction(node *ast.Node, check // contextual type may recursively get back to here during overload resolution of the call. If so, we will have // already assigned contextual types. if links.flags&NodeCheckFlagsContextChecked == 0 { - links.flags |= NodeCheckFlagsContextChecked - signature := c.getSignatureFromDeclarationUncontextual(node) + setFlagMemoBits(c, memoKeyOf(node, memoContextualParameters), &links.flags, NodeCheckFlagsContextChecked, NodeCheckFlagsContextChecked) + // A JSDoc @type tag on the function supplies the signature of its + // symbol, and the types of its parameters. + signature := c.getSignatureOfFullSignatureType(node) + if signature == nil { + signature = c.getSignatureFromDeclarationUncontextual(node) + } + c.beginTypeResolution(node, TypeSystemPropertyNameContextChecked, nil) if c.isContextSensitive(node) { if contextualSignature != nil { inferenceContext := c.getInferenceContext(node) @@ -10576,8 +10890,11 @@ func (c *Checker) assignContextualParameterTypesOfFunction(node *ast.Node, check c.inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext) } } + c.popTypeResolution() + return } } + c.observeResolutionInProgress(node, TypeSystemPropertyNameContextChecked) } func (c *Checker) checkFunctionExpressionOrObjectLiteralMethodDeferred(node *ast.Node) { @@ -10732,6 +11049,9 @@ func (c *Checker) assignContextualParameterTypes(sig *Signature, context *Signat // This signature has already has a contextual inference performed and cached on it return } + if c.logResults() { + c.logRestore(func() { sig.setTypeParameters(c, nil) }) + } sig.setTypeParameters(c, context.TypeParameters()) } if context.ThisParameter() != nil { @@ -10783,7 +11103,7 @@ func (c *Checker) assignNonContextualParameterTypes(signature *Signature) { func (c *Checker) assignParameterType(parameter *ast.Symbol, contextualType *Type) { links := c.valueSymbolLinks.Get(parameter) - if links.resolvedType != nil { + if links.resolvedType.get(c) != nil { return } declaration := parameter.ValueDeclaration() @@ -10795,13 +11115,13 @@ func (c *Checker) assignParameterType(parameter *ast.Symbol, contextualType *Typ t = c.getTypeOfSymbol(parameter) } } - links.resolvedType = c.addOptionalityEx(t, false, declaration != nil && declaration.Initializer() == nil && isOptionalDeclaration(declaration)) + links.resolvedType.set(c, c.addOptionalityEx(t, false, declaration != nil && declaration.Initializer() == nil && isOptionalDeclaration(declaration))) if declaration != nil && !ast.IsIdentifier(declaration.Name()) { // if inference didn't come up with anything but unknown, fall back to the binding pattern if present. - if links.resolvedType == c.unknownType { - links.resolvedType = c.getTypeFromBindingPattern(declaration.Name(), false, false) + if links.resolvedType.get(c) == c.unknownType { + links.resolvedType.set(c, c.getTypeFromBindingPattern(declaration.Name(), false, false)) } - c.assignBindingElementTypes(declaration.Name(), links.resolvedType) + c.assignBindingElementTypes(declaration.Name(), links.resolvedType.get(c)) } } @@ -10813,7 +11133,7 @@ func (c *Checker) assignBindingElementTypes(pattern *ast.Node, parentType *Type) if name != nil { t := c.getBindingElementTypeFromParentType(element, parentType, false /*noTupleBoundsCheck*/) if ast.IsIdentifier(name) { - c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(element)).resolvedType = t + c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(element)).resolvedType.set(c, t) } else { c.assignBindingElementTypes(name, t) } @@ -10919,7 +11239,7 @@ func (c *Checker) setNodeLinksForPrivateIdentifierScope(node *ast.Node) { func (c *Checker) recordPotentialCollisionWithWeakMapSetInGeneratedCode(node *ast.Node, name *ast.Node) { if c.languageVersion <= core.ScriptTargetES2021 && (c.needCollisionCheckForIdentifier(node, name, "WeakMap") || c.needCollisionCheckForIdentifier(node, name, "WeakSet")) { - c.addDeferredDiagnostic(func() { + c.addDeferredDiagnostic(func(c *Checker) { c.checkWeakMapSetCollision(node) }) } @@ -10953,7 +11273,7 @@ func (c *Checker) checkCollisionWithGlobalPromiseInGeneratedCode(node *ast.Node, func (c *Checker) recordPotentialCollisionWithReflectInGeneratedCode(node *ast.Node, name *ast.Node) { if name != nil && c.languageVersion <= core.ScriptTargetES2021 && c.needCollisionCheckForIdentifier(node, name, "Reflect") { - c.addDeferredDiagnostic(func() { + c.addDeferredDiagnostic(func(c *Checker) { c.checkReflectCollision(node) }) } @@ -11043,9 +11363,26 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) return exprType } key := InstantiationExpressionKey{nodeId: ast.GetNodeId(node), typeId: exprType.id} - if cached := c.instantiationExpressionTypes[key]; cached != nil { - return cached - } + entry, _ := c.instantiationExpressionTypes.get(key) + if entry == nil { + entry = &instantiationExpressionEntry{} + c.instantiationExpressionTypes.intern(key, entry) + } + if entry.t != nil { + c.observeFlagMemo(memoKeyOf(entry, memoInstantiationExpressionType)) + return entry.t + } + c.beginMemo(memoKeyOf(entry, memoInstantiationExpressionType)) + result := c.getInstantiationExpressionTypeUncached(exprType, node, typeArguments, entry) + c.endMemo() + return result +} + +type instantiationExpressionEntry struct { + t *Type +} + +func (c *Checker) getInstantiationExpressionTypeUncached(exprType *Type, node *ast.Node, typeArguments *ast.NodeList, entry *instantiationExpressionEntry) *Type { hasSomeApplicableSignature := false var nonApplicableType *Type getInstantiatedSignatures := func(signatures []*Signature) []*Signature { @@ -11077,7 +11414,7 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) debug.Assert(t.symbol != nil, "Instantiation expression source type must have a symbol") symbol.SetDeclarations(t.symbol.Declarations()) result := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType, symbol) - c.setStructuredTypeMembers(result, resolved.getMembers(), callSignatures, constructSignatures, resolved.getIndexInfos()) + c.setStructuredTypeMembers(result, resolved.getMembers(c), callSignatures, constructSignatures, resolved.getIndexInfos()) result.AsInstantiationExpressionType().node = node return result } @@ -11106,7 +11443,7 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) return result } result := getInstantiatedType(exprType) - c.instantiationExpressionTypes[key] = result + c.setFlagMemoValue(memoKeyOf(entry, memoInstantiationExpressionType), &entry.t, result) var errorType *Type if hasSomeApplicableSignature { errorType = nonApplicableType @@ -11452,8 +11789,9 @@ func (c *Checker) checkIdentifier(node *ast.Node, checkMode CheckMode) *Type { // If the identifier is declared in a binding pattern for which we're currently computing the implied type and the // reference occurs with the same binding pattern, return the non-inferrable any type. This for example occurs in // 'const [a, b = a + 1] = [2]' when we're computing the contextual type for the array literal '[2]'. - if declaration != nil && declaration.Kind == ast.KindBindingElement && slices.Contains(c.contextualBindingPatterns, declaration.Parent) && - ast.FindAncestor(node, func(parent *ast.Node) bool { return parent == declaration.Parent }) != nil { + if declaration != nil && declaration.Kind == ast.KindBindingElement && + ast.FindAncestor(node, func(parent *ast.Node) bool { return parent == declaration.Parent }) != nil && + c.isResolvingAnywhere(declaration.Parent, TypeSystemPropertyNameContextualBindingPattern) { return c.nonInferrableAnyType } t := c.getNarrowedTypeOfSymbol(localOrExportSymbol, node) @@ -11602,9 +11940,16 @@ func (c *Checker) removeOptionalityFromDeclaredType(declaredType *Type, declarat } func (c *Checker) parameterInitializerContainsUndefined(declaration *ast.Node) bool { + c.beginMemo(memoKeyOf(declaration, memoInitializerIsUndefined)) + result := c.parameterInitializerContainsUndefinedUncached(declaration) + c.endMemo() + return result +} + +func (c *Checker) parameterInitializerContainsUndefinedUncached(declaration *ast.Node) bool { links := c.nodeLinks.Get(declaration) if links.flags&NodeCheckFlagsInitializerIsUndefinedComputed == 0 { - if !c.pushTypeResolution(declaration, TypeSystemPropertyNameInitializerIsUndefined) { + if !c.pushTypeResolution(declaration, TypeSystemPropertyNameInitializerIsUndefined, nil) { c.reportCircularityError(declaration.Symbol()) return true } @@ -11614,7 +11959,7 @@ func (c *Checker) parameterInitializerContainsUndefined(declaration *ast.Node) b return true } if links.flags&NodeCheckFlagsInitializerIsUndefinedComputed == 0 { - links.flags |= NodeCheckFlagsInitializerIsUndefinedComputed | core.IfElse(containsUndefined, NodeCheckFlagsInitializerIsUndefined, 0) + setFlagMemoBits(c, memoKeyOf(declaration, memoInitializerIsUndefined), &links.flags, NodeCheckFlagsInitializerIsUndefinedComputed|NodeCheckFlagsInitializerIsUndefined, NodeCheckFlagsInitializerIsUndefinedComputed|core.IfElse(containsUndefined, NodeCheckFlagsInitializerIsUndefined, 0)) } } return links.flags&NodeCheckFlagsInitializerIsUndefined != 0 @@ -11729,7 +12074,7 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l return c.anyType } if right.Text() != "" && !c.checkAndReportErrorForExtendingInterface(node) { - c.addDeferredDiagnostic(func() { + c.addDeferredDiagnostic(func(c *Checker) { // must be deferred because reporting this error can cause us to materialize the containing type completely (to print it), leading to erroneous circularity errors c.reportNonexistentProperty(right, core.IfElse(isThisTypeParameter(leftType), apparentType, leftType), isUncheckedJS) }) @@ -11756,7 +12101,7 @@ func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, l } c.checkPropertyNotUsedBeforeDeclaration(prop, node, right) c.markPropertyAsReferenced(prop, node, c.isSelfTypeAccess(left, parentSymbol)) - c.symbolNodeLinks.Get(node).resolvedSymbol = prop + setIfChanged(c, &c.symbolNodeLinks.Get(node).resolvedSymbol, prop) c.checkPropertyAccessibility(node, left.Kind == ast.KindSuperKeyword, ast.IsWriteAccess(node), apparentType, prop) if c.isAssignmentToReadonlyEntity(node, prop, assignmentKind) { c.error(right, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, right.Text()) @@ -11914,16 +12259,16 @@ func (c *Checker) checkPrivateIdentifierPropertyAccess(leftType *Type, right *as } func (c *Checker) reportNonexistentProperty(propNode *ast.Node, containingType *Type, isUncheckedJS bool) { - key := NonExistentPropertyKey{propNode, containingType, isUncheckedJS} - if c.nonExistentProperties.Has(key) { + if !c.checking { + c.recordEffect(func(c *Checker) { c.reportNonexistentProperty(propNode, containingType, isUncheckedJS) }) return } - c.nonExistentProperties.Add(key) - links := c.nodeLinks.Get(propNode) - if links.flags&NodeCheckFlagsTypeChecked != 0 { - return // error already made/in progress + // Only the first report for a name is made, even if it is reported again + // on another containing type. + if c.nonExistentPropertyNodes.Has(propNode) { + return } - links.flags |= NodeCheckFlagsTypeChecked + c.nonExistentPropertyNodes.Add(propNode) if ast.IsJSDocNameReferenceContext(propNode) { return } @@ -12687,7 +13032,7 @@ func (c *Checker) checkAssertion(node *ast.Node, checkMode CheckMode) *Type { var exprType *Type if isConst { exprType = c.checkExpressionEx(node.Expression(), checkMode) - } else if c.resolvingAssertionOperands.AddIfAbsent(node) { + } else if !c.isResolvingAnywhere(node, TypeSystemPropertyNameAssertionOperand) { // Only the deferred check uses the operand type, and it uses the // cached one, so compute that here rather than checking twice. The // operand is contextually typed by the asserted type alone; hiding @@ -12697,10 +13042,11 @@ func (c *Checker) checkAssertion(node *ast.Node, checkMode CheckMode) *Type { // assertion is re-entered when the operand's flow type depends on // a call containing it within a loop; the enclosing computation // then provides the cached type. + c.beginTypeResolution(node, TypeSystemPropertyNameAssertionOperand, nil) c.pushInferenceContext(node.Expression(), nil) c.checkExpressionCached(node.Expression()) c.popInferenceContext() - c.resolvingAssertionOperands.Delete(node) + c.popTypeResolution() } // Always check the type node so its identifiers are resolved. resolveName knows not // to resolve (or report an error for) the `const` in a `const` assertion, so this is @@ -13049,6 +13395,14 @@ func (c *Checker) checkObjectLiteralDestructuringPropertyAssignment(node *ast.No return c.checkDestructuringAssignment(property.Expression(), t, CheckModeNormal, false) } c.error(property, diagnostics.Property_assignment_expected) + // The member is still checked as it would be in an object literal, as + // it is when anything computes the type of the literal. + switch property.Kind { + case ast.KindMethodDeclaration: + c.checkObjectLiteralMethod(property, CheckModeNormal) + case ast.KindGetAccessor, ast.KindSetAccessor: + c.checkNodeDeferred(property) + } return nil } @@ -13170,7 +13524,7 @@ func (c *Checker) checkAssignmentOperator(left *ast.Node, operator ast.Kind, rig if ast.IsAssignmentOperator(operator) { // We ignore assignments of undefined to CommonJS exports when there are multiple assignment declarations if ast.IsDeclarationNode(left.Parent) && ast.GetAssignmentDeclarationKind(left.Parent) == ast.JSDeclarationKindExportsProperty { - if symbol := c.symbolNodeLinks.Get(left).resolvedSymbol; symbol != nil && len(symbol.Declarations()) > 1 && rightType.flags&TypeFlagsUndefined != 0 { + if symbol := c.symbolNodeLinks.Get(left).resolvedSymbol.get(c); symbol != nil && len(symbol.Declarations()) > 1 && rightType.flags&TypeFlagsUndefined != 0 { return } } @@ -13498,7 +13852,7 @@ func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, } // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type // which provides us with the opportunity to emit more detailed errors - if c.symbolNodeLinks.Get(left).resolvedSymbol == nil && ast.GetContainingClass(left) != nil { + if c.symbolNodeLinks.Get(left).resolvedSymbol.get(c) == nil && ast.GetContainingClass(left) != nil { isUncheckedJS := c.isUncheckedJSSuggestion(left, rightType.symbol, true /*excludeClasses*/) c.reportNonexistentProperty(left, rightType, isUncheckedJS) } @@ -13579,7 +13933,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNone) var contextualTypeHasPattern bool if contextualType != nil { - if pattern := c.patternForType[contextualType]; pattern != nil && (ast.IsObjectBindingPattern(pattern) || ast.IsObjectLiteralExpression(pattern)) { + if pattern, _ := c.patternForType.get(contextualType); pattern != nil && (ast.IsObjectBindingPattern(pattern) || ast.IsObjectLiteralExpression(pattern)) { contextualTypeHasPattern = true } } @@ -13621,16 +13975,18 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type if patternWithComputedProperties { flags |= ObjectFlagsObjectLiteralPatternWithComputedProperties } - key := c.getObjectLiteralTypeKey(node, flags, propertiesTable, indexInfos) - if existing := c.objectLiteralTypes[key]; existing != nil { - return existing + existingTypes, _ := c.objectLiteralTypes.get(node) + for _, existing := range existingTypes { + if c.isSameObjectLiteralType(existing, flags, propertiesTable, indexInfos) { + return existing.t + } } result := c.newAnonymousType(node.Symbol(), propertiesTable, nil, nil, indexInfos) result.objectFlags |= flags if inDestructuringPattern { - c.patternForType[result] = node + c.patternForType.intern(result, node) } - c.objectLiteralTypes[key] = result + c.objectLiteralTypes.intern(node, append(existingTypes, objectLiteralType{flags, propertiesTable, indexInfos, result})) return result } for _, memberDecl := range node.Properties() { @@ -13681,7 +14037,7 @@ func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type prop.SetDeclarations(member.Declarations()) prop.SetParent(member.Parent()) prop.SetValueDeclaration(member.ValueDeclaration()) - links.resolvedType = t + links.resolvedType.setIdentity(c, t) links.target = member member = prop if allPropertiesTable != nil { @@ -13880,7 +14236,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj b.writeByte(0) } key := b.hash() - if existing := c.spreadTypes[key]; existing != nil { + if existing, _ := c.spreadTypes.get(c, key); existing != nil { return existing } members := ast.NewSymbolTable() @@ -13908,7 +14264,7 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj rightType := c.getTypeOfSymbol(rightProp) if rightProp.Flags()&ast.SymbolFlagsOptional != 0 { key := spreadSymbolKey{left: leftProp, right: rightProp} - if existing := c.spreadSymbols[key]; existing != nil { + if existing, _ := c.spreadSymbols.get(c, key); existing != nil { members.Set(leftProp.Name(), existing) continue } @@ -13924,15 +14280,15 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj leftTypeWithoutUndefined := c.removeMissingOrUndefinedType(leftType) rightTypeWithoutUndefined := c.removeMissingOrUndefinedType(rightType) if leftTypeWithoutUndefined == rightTypeWithoutUndefined { - links.resolvedType = leftType + links.resolvedType.setIdentity(c, leftType) } else { - links.resolvedType = c.getUnionTypeEx([]*Type{leftType, rightTypeWithoutUndefined}, UnionReductionSubtype, nil, nil) + links.resolvedType.setIdentity(c, c.getUnionTypeEx([]*Type{leftType, rightTypeWithoutUndefined}, UnionReductionSubtype, nil, nil)) } c.spreadLinks.Get(result).leftSpread = leftProp c.spreadLinks.Get(result).rightSpread = rightProp result.SetDeclarations(declarations) links.setNameType(c, c.valueSymbolLinks.Get(leftProp).getNameType()) - c.spreadSymbols[key] = result + c.spreadSymbols.set(c, key, result) members.Set(leftProp.Name(), result) } } else { @@ -13946,14 +14302,14 @@ func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, obj // Different inputs can produce the same members, e.g. when the right side // overrides every property of the left side. contentKey := c.getObjectTypeContentKey(symbol, flags, members, spreadIndexInfos) - if existing := c.objectContentTypes[contentKey]; existing != nil { - c.spreadTypes[key] = existing + if existing, _ := c.objectContentTypes.get(contentKey); existing != nil { + c.spreadTypes.set(c, key, existing) return existing } spread := c.newAnonymousType(symbol, members, nil, nil, spreadIndexInfos) spread.objectFlags |= flags - c.objectContentTypes[contentKey] = spread - c.spreadTypes[key] = spread + c.objectContentTypes.intern(contentKey, spread) + c.spreadTypes.set(c, key, spread) return spread } @@ -14024,9 +14380,9 @@ func (c *Checker) tryMergeUnionOfObjectTypeAndEmptyObject(t *Type, readonly bool result := c.newSymbolEx(flags, prop.Name(), prop.CheckFlags()&ast.CheckFlagsLate|core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) links := c.valueSymbolLinks.Get(result) if isSetonlyAccessor { - links.resolvedType = c.undefinedType + links.resolvedType.setIdentity(c, c.undefinedType) } else { - links.resolvedType = c.addOptionalityEx(c.getTypeOfSymbol(prop), true /*isProperty*/, true /*isOptional*/) + links.resolvedType.setIdentity(c, c.addOptionalityEx(c.getTypeOfSymbol(prop), true /*isProperty*/, true /*isOptional*/)) } result.SetDeclarations(prop.Declarations()) links.setNameType(c, c.valueSymbolLinks.Get(prop).getNameType()) @@ -14051,21 +14407,21 @@ func (c *Checker) getSpreadSymbol(prop *ast.Symbol, readonly bool) *ast.Symbol { return prop } key := spreadSymbolKey{right: prop, readonly: readonly} - if existing := c.spreadSymbols[key]; existing != nil { + if existing, _ := c.spreadSymbols.get(c, key); existing != nil { return existing } flags := ast.SymbolFlagsProperty | (prop.Flags() & ast.SymbolFlagsOptional) result := c.newSymbolEx(flags, prop.Name(), prop.CheckFlags()&ast.CheckFlagsLate|core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) links := c.valueSymbolLinks.Get(result) if isSetonlyAccessor { - links.resolvedType = c.undefinedType + links.resolvedType.setIdentity(c, c.undefinedType) } else { - links.resolvedType = c.getTypeOfSymbol(prop) + links.resolvedType.setIdentity(c, c.getTypeOfSymbol(prop)) } result.SetDeclarations(prop.Declarations()) links.setNameType(c, c.valueSymbolLinks.Get(prop).getNameType()) c.mappedSymbolLinks.Get(result).syntheticOrigin = prop - c.spreadSymbols[key] = result + c.spreadSymbols.set(c, key, result) return result } @@ -14293,16 +14649,15 @@ func (c *Checker) getNarrowedTypeOfSymbol(symbol *ast.Symbol, location *ast.Node } parent := declaration.Parent.Parent if ast.IsVariableDeclaration(rootDeclaration) && c.getCombinedNodeFlagsCached(rootDeclaration)&ast.NodeFlagsConstant != 0 || ast.IsParameterDeclaration(rootDeclaration) { - links := c.nodeLinks.Get(parent) - if links.flags&NodeCheckFlagsInCheckIdentifier == 0 { - links.flags |= NodeCheckFlagsInCheckIdentifier + if !c.isResolvingAnywhere(parent, TypeSystemPropertyNameBindingElementParent) { + c.beginTypeResolution(parent, TypeSystemPropertyNameBindingElementParent, nil) parentType := c.getTypeForBindingElementParent(parent, CheckModeNormal) var parentTypeConstraint *Type if parentType != nil { parentTypeConstraint = c.mapType(parentType, c.getBaseConstraintOrType) } // Guard parent-type resolution only; flow analysis should allow re-entrant narrowing - links.flags &^= NodeCheckFlagsInCheckIdentifier + c.popTypeResolution() if parentTypeConstraint != nil && parentTypeConstraint.flags&TypeFlagsUnion != 0 && !(ast.IsParameterDeclaration(rootDeclaration) && c.isSomeSymbolAssigned(rootDeclaration)) { pattern := declaration.Parent narrowedType := c.getFlowTypeOfReferenceEx(pattern, parentTypeConstraint, parentTypeConstraint, nil /*flowContainer*/, getFlowNodeOfNode(location)) @@ -14423,23 +14778,34 @@ func (c *Checker) checkExpressionForMutableLocation(node *ast.Node, checkMode Ch func (c *Checker) getResolvedSymbol(node *ast.Node) *ast.Symbol { links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol == nil { + if symbol := links.resolvedSymbol.get(c); symbol != nil { + return symbol + } + links.resolvedSymbol.begin(c) + result := c.getResolvedSymbolUncached(node) + c.endMemo() + return result +} + +func (c *Checker) getResolvedSymbolUncached(node *ast.Node) *ast.Symbol { + links := c.symbolNodeLinks.Get(node) + if links.resolvedSymbol.get(c) == nil { var symbol *ast.Symbol if !ast.NodeIsMissing(node) { symbol = c.resolveName(node, node.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, c.getCannotFindNameDiagnosticForName(node), !ast.IsWriteOnlyAccess(node), false /*excludeGlobals*/) } - links.resolvedSymbol = core.OrElse(symbol, c.unknownSymbol) + links.resolvedSymbol.set(c, core.OrElse(symbol, c.unknownSymbol)) } - return links.resolvedSymbol + return links.resolvedSymbol.get(c) } func (c *Checker) getResolvedSymbolOrNil(node *ast.Node) *ast.Symbol { - return c.symbolNodeLinks.Get(node).resolvedSymbol + return c.symbolNodeLinks.Get(node).resolvedSymbol.get(c) } func (c *Checker) getReferencedValueOrAliasSymbol(reference *ast.Node) *ast.Symbol { - resolvedSymbol := c.symbolNodeLinks.Get(reference).resolvedSymbol + resolvedSymbol := c.symbolNodeLinks.Get(reference).resolvedSymbol.get(c) if resolvedSymbol != nil && resolvedSymbol != c.unknownSymbol { return resolvedSymbol } @@ -14502,24 +14868,34 @@ func (c *Checker) getDiagnostics(ctx context.Context, sourceFile *ast.SourceFile func (c *Checker) GetGlobalDiagnostics() []*ast.Diagnostic { c.checkNotCanceled() - c.produceDeferredDiagnostics() + c.withChecking(c.produceDeferredDiagnostics) return c.diagnostics.GetGlobalDiagnostics() } -func (c *Checker) addDeferredDiagnostic(callback func()) { +// Deferred callbacks receive the checker that produces them, since the one +// that queued them may be a checker answering queries sharing this store. +func (c *Checker) addDeferredDiagnostic(callback func(c *Checker)) { + if !c.checking { + c.recordEffect(func(c *Checker) { c.addDeferredDiagnostic(callback) }) + return + } c.deferredDiagnosticCallbacks = append(c.deferredDiagnosticCallbacks, callback) } func (c *Checker) produceDeferredDiagnostics() { for _, cb := range c.deferredDiagnosticCallbacks { - cb() + cb(c) } c.deferredDiagnosticCallbacks = nil } func (c *Checker) addDiagnostic(diagnostic *ast.Diagnostic) *ast.Diagnostic { // Discard diagnostics created while at the maximum number of recursive TypeToString invocations. - if c.serializationLevel < maxSerializationLevel { + if !c.depthLimitReached(&c.serializationLevel, maxSerializationLevel) { + if !c.checking { + c.recordEffect(func(c *Checker) { c.diagnostics.Add(diagnostic) }) + return diagnostic + } return c.diagnostics.Add(diagnostic) } return diagnostic @@ -14527,7 +14903,11 @@ func (c *Checker) addDiagnostic(diagnostic *ast.Diagnostic) *ast.Diagnostic { func (c *Checker) addSuggestionDiagnostic(diagnostic *ast.Diagnostic) *ast.Diagnostic { // Discard diagnostics created while at the maximum number of recursive TypeToString invocations. - if c.serializationLevel < maxSerializationLevel { + if !c.depthLimitReached(&c.serializationLevel, maxSerializationLevel) { + if !c.checking { + c.recordEffect(func(c *Checker) { c.suggestionDiagnostics.Add(diagnostic) }) + return diagnostic + } return c.suggestionDiagnostics.Add(diagnostic) } return diagnostic @@ -14537,6 +14917,50 @@ func (c *Checker) error(location *ast.Node, message *diagnostics.Message, args . return c.addDiagnostic(NewDiagnosticForNode(location, message, args...)) } +// errorAtCurrentNode reports an error at the node currently being checked. +// When a result is computed outside of checking, there is no such node; the +// location is then resolved when checking first observes the result, which +// is where the error would have been reported had checking computed it. +func (c *Checker) errorAtCurrentNode(message *diagnostics.Message, args ...any) { + c.reportAtCurrentNode(func(c *Checker, node *ast.Node) { c.error(node, message, args...) }) +} + +// reportAtCurrentNode reports an error located at whichever node's check +// reached it, so that every node reaching it reports it, as it would if it +// were checked first. Results cached from now until the current node is left +// were computed for that node, and are discarded so that other nodes +// compute them again. +func (c *Checker) reportAtCurrentNode(report func(c *Checker, node *ast.Node)) { + c.reportNode(¤tNodeError{report}) +} + +func (c *Checker) reportNode(r *currentNodeError) { + c.markCurrentNodeCircular() + if c.currentNodeErrorCollectors != 0 { + c.currentNodeErrors = append(c.currentNodeErrors, r) + } + c.reportAtCurrentNodeOnce(r.report) +} + +// reportAtCurrentNodeOnce is reportAtCurrentNode for an error whose result +// is kept, so that only the node reaching it first reports it. +func (c *Checker) reportAtCurrentNodeOnce(report func(c *Checker, node *ast.Node)) { + if !c.checking { + // A node the computation started at is where the computation was + // requested from, so it is located wherever the result is reused. + node := c.currentNode + if c.atMemoStartNode() { + node = nil + } + if c.memoTop != 0 { + c.resolutions[c.memoTop-1].reportsAtCurrentNode = true + } + c.recordEffect(func(c *Checker) { report(c, core.OrElse(node, c.currentNode)) }) + return + } + report(c, c.currentNode) +} + func (c *Checker) errorSkippedOnNoEmit(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { diagnostic := c.error(location, message, args...) diagnostic.SetSkippedOnNoEmit() @@ -14625,13 +15049,13 @@ func (c *Checker) newSymbolEx(flags ast.SymbolFlags, name string, checkFlags ast func (c *Checker) newParameter(name string, t *Type) *ast.Symbol { symbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, name) - c.valueSymbolLinks.Get(symbol).resolvedType = t + c.valueSymbolLinks.Get(symbol).resolvedType.setIdentity(c, t) return symbol } func (c *Checker) newProperty(name string, t *Type) *ast.Symbol { symbol := c.newSymbol(ast.SymbolFlagsProperty, name) - c.valueSymbolLinks.Get(symbol).resolvedType = t + c.valueSymbolLinks.Get(symbol).resolvedType.setIdentity(c, t) return symbol } @@ -14642,8 +15066,18 @@ func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTa if second.Len() == 0 { return first } - combined := ast.NewSymbolTable() - c.mergeSymbolTable(combined, first, false, nil) + // Late binding has merged into early symbols already; the merges are + // looked up without resolving the table, whose resolution is this one. + combined := ast.NewSymbolTableWithCapacity(first.Len() + second.Len()) + for id, symbol := range first.All() { + if merged := c.mergedSymbolOf(symbol); merged != nil { + symbol = merged + } + if merged, _ := c.lateMergedSymbols.get(c, symbol); merged != nil { + symbol = merged + } + combined.Set(id, symbol) + } c.mergeSymbolTable(combined, second, false, nil) return combined } @@ -14889,24 +15323,124 @@ func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.Symbol { } func (c *Checker) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol != nil { - if merged := c.mergedSymbols.TryGet(symbol); merged != nil && *merged != nil { - return *merged - } + if symbol == nil { + return nil + } + if merged := c.mergedSymbolOf(symbol); merged != nil { + symbol = merged + } + if symbol.Flags()&(ast.SymbolFlagsClassMember|ast.SymbolFlagsSignature) != 0 { + return c.getLateMergedSymbol(symbol) } return symbol } -func (c *Checker) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol.Parent() != nil { - return c.getMergedSymbol(c.getLateBoundSymbol(symbol.Parent())) +// getLateMergedSymbol returns the symbol a member is merged into when its +// parent's late-bound members are resolved, resolving them if they could +// merge with the member. The answer is thus the same whether or not anything +// has resolved the parent's members yet. +func (c *Checker) getLateMergedSymbol(symbol *ast.Symbol) *ast.Symbol { + if !c.mergesFrozen || symbol.Parent() == nil { + return symbol } - return nil + late := symbol.CheckFlags()&ast.CheckFlagsLate != 0 + var lateBindable uint8 + if !late { + // Most members cannot merge, which depends only on syntax and on + // the merges done before checking, so it is looked up cheaply. + if symbol.Parent() != c.lastLateBindableParent { + known := c.parentLateBindableKinds.Get(symbol.Parent()) + if *known == 0 { + *known = c.getLateBindableKinds(c.getMergedSymbol(symbol.Parent())) | lateBindableKnown + } + c.lastLateBindableParent = symbol.Parent() + c.lastLateBindableKinds = *known &^ lateBindableKnown + } + lateBindable = c.lastLateBindableKinds + if lateBindable == 0 { + return symbol + } + } + parent := c.getMergedSymbol(symbol.Parent()) + var kind MembersOrExportsResolutionKind + switch symbol { + case parent.Members().Get(symbol.Name()): + kind = MembersOrExportsResolutionKindResolvedMembers + case parent.Exports().Get(symbol.Name()): + kind = MembersOrExportsResolutionKindResolvedExports + default: + if !late { + return symbol + } + // Late-bound symbols are created by resolving their parent's members. + } + if !late { + if lateBindable&(1<= 0 { + return c.yieldMembers(key, symbol, resolutionKind, floor) + } + c.beginMemo(key) + c.currentResolution().memberSymbol = symbol + result := c.getResolvedMembersOrExportsOfSymbolUncached(symbol, resolutionKind) + c.endMemo() + return result +} + +// findEarlierMembersInCycle handles re-entering the member table of symbol, +// identified by key, while it is resolved. Re-entry observes the part of the +// table published so far, so when the tables of several symbols are resolved +// in a cycle, the result depends on which table was entered first. If the +// table of a symbol declared before this one is resolved since this table +// was entered, it returns the index in resolutions of the first declared, +// the table the cycle canonically enters at; otherwise -1. +func (c *Checker) findEarlierMembersInCycle(key memoKey, symbol *ast.Symbol) int { + i, ok := c.resolving.get(key) + if !ok || int(i) < c.visibleStart || c.isYieldingTo(key, TypeSystemPropertyNameStructuredMembers) { + return -1 + } + for j := int(i) + 1; j < len(c.resolutions); j++ { + f := &c.resolutions[j] + if f.memberSymbol != nil && c.compareSymbols(f.memberSymbol, symbol) < 0 { + return int(i) + 1 + } + } + return -1 +} + +// yieldMembers computes the member table of symbol again, as if the cycle +// in progress had been entered at the table of a symbol declared before it: +// with the computation of the table in progress, which starts at floor, +// hidden. Re-entry observes the incomplete table, so what is computed since +// it started is in a cycle. The table in progress is the one cached once it +// is complete, so whatever computing it again caches is discarded, keeping +// only its result and effects for the caller. +func (c *Checker) yieldMembers(key memoKey, symbol *ast.Symbol, resolutionKind MembersOrExportsResolutionKind, floor int) ast.SymbolTable { + c.markCircular(floor - 1) + mark := c.markDiscard() + links := c.membersAndExportsLinks.Get(symbol) + saved := links[resolutionKind] + links[resolutionKind] = nil + c.beginCycleYield(key, TypeSystemPropertyNameStructuredMembers, -1, floor) + c.beginMemo(key) + c.currentResolution().memberSymbol = symbol + result := c.resolveMembersOrExportsOfSymbol(symbol, resolutionKind, links) + c.endMemoUncached() + c.endCycleYield() + links[resolutionKind] = saved + c.discardLogSince(mark.discardLog) + c.releaseDiscardMark(mark) + return result +} + +func (c *Checker) getResolvedMembersOrExportsOfSymbolUncached(symbol *ast.Symbol, resolutionKind MembersOrExportsResolutionKind) ast.SymbolTable { links := c.membersAndExportsLinks.Get(symbol) if links[resolutionKind] == nil { - isStatic := resolutionKind == MembersOrExportsResolutionKindResolvedExports - earlySymbols := symbol.Exports() - switch { - case !isStatic: - earlySymbols = symbol.Members() - case symbol.Flags()&ast.SymbolFlagsModule != 0: - earlySymbols, _ = c.getExportsOfModuleWorker(symbol) - } - links[resolutionKind] = earlySymbols - // fill in any as-yet-unresolved late-bound members. - var lateSymbols ast.SymbolTable - for _, decl := range symbol.Declarations() { - for _, member := range getMembersOfDeclaration(decl) { - if isStatic == ast.HasStaticModifier(member) { - switch { - case c.hasLateBindableName(member): - if lateSymbols == nil { - lateSymbols = ast.NewSymbolTable() - } - c.lateBindMember(symbol, earlySymbols, lateSymbols, member) - case c.hasLateBindableIndexSignature(member): - if lateSymbols == nil { - lateSymbols = ast.NewSymbolTable() - } - c.lateBindIndexSignature(symbol, earlySymbols, lateSymbols, member.AsNode() /* as LateBoundDeclaration | LateBoundBinaryExpressionDeclaration */) + c.resolveMembersOrExportsOfSymbol(symbol, resolutionKind, links) + if c.logResults() { + c.logMemoDiscard(nil, memoKeyOf(symbol, memoResolvedMembers+memoField(resolutionKind)), func() { links[resolutionKind] = nil }) + } + } + return links[resolutionKind] +} + +func (c *Checker) resolveMembersOrExportsOfSymbol(symbol *ast.Symbol, resolutionKind MembersOrExportsResolutionKind, links *MembersAndExportsLinks) ast.SymbolTable { + isStatic := resolutionKind == MembersOrExportsResolutionKindResolvedExports + earlySymbols := symbol.Exports() + switch { + case !isStatic: + earlySymbols = symbol.Members() + case symbol.Flags()&ast.SymbolFlagsModule != 0: + earlySymbols, _ = c.getExportsOfModuleWorker(symbol) + } + links[resolutionKind] = earlySymbols + // fill in any as-yet-unresolved late-bound members. + var lateSymbols ast.SymbolTable + for _, decl := range symbol.Declarations() { + for _, member := range getMembersOfDeclaration(decl) { + if isStatic == ast.HasStaticModifier(member) { + switch { + case c.hasLateBindableName(member): + if lateSymbols == nil { + lateSymbols = ast.NewSymbolTable() + } + c.lateBindMember(symbol, earlySymbols, lateSymbols, member) + case c.hasLateBindableIndexSignature(member): + if lateSymbols == nil { + lateSymbols = ast.NewSymbolTable() } + c.lateBindIndexSignature(symbol, earlySymbols, lateSymbols, member.AsNode() /* as LateBoundDeclaration | LateBoundBinaryExpressionDeclaration */) } } } - if isStatic { - if assignmentSymbol := symbol.Exports().Get(ast.InternalSymbolNameAssignmentDeclaration); assignmentSymbol != nil { - for _, member := range assignmentSymbol.Declarations() { - if c.hasLateBindableName(member) { - if lateSymbols == nil { - lateSymbols = ast.NewSymbolTable() - } - c.lateBindMember(symbol, earlySymbols, lateSymbols, member) + } + if isStatic { + if assignmentSymbol := symbol.Exports().Get(ast.InternalSymbolNameAssignmentDeclaration); assignmentSymbol != nil { + for _, member := range assignmentSymbol.Declarations() { + if c.hasLateBindableName(member) { + if lateSymbols == nil { + lateSymbols = ast.NewSymbolTable() } + c.lateBindMember(symbol, earlySymbols, lateSymbols, member) } } } - links[resolutionKind] = c.combineSymbolTables(earlySymbols, lateSymbols) } + links[resolutionKind] = c.combineSymbolTables(earlySymbols, lateSymbols) return links[resolutionKind] } @@ -16687,10 +17299,10 @@ func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *ast.Symbol, resolu func (c *Checker) lateBindMember(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) *ast.Symbol { debug.Assert(decl.Symbol() != nil, "The member is expected to have a symbol.") links := c.symbolNodeLinks.Get(decl) - if links.resolvedSymbol == nil { + if links.resolvedSymbol.get(c) == nil { // In the event we attempt to resolve the late-bound name of this member recursively, // fall back to the early-bound name of this member. - links.resolvedSymbol = decl.Symbol() + links.resolvedSymbol.set(c, decl.Symbol()) var declName *ast.Node if ast.IsBinaryExpression(decl) { declName = decl.AsBinaryExpression().Left @@ -16741,10 +17353,10 @@ func (c *Checker) lateBindMember(parent *ast.Symbol, earlySymbols ast.SymbolTabl if lateSymbol.Parent() == nil { lateSymbol.SetParent(parent) } - links.resolvedSymbol = lateSymbol + links.resolvedSymbol.set(c, lateSymbol) } } - return links.resolvedSymbol + return links.resolvedSymbol.get(c) } func (c *Checker) lateBindIndexSignature(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) { @@ -16812,10 +17424,22 @@ func (c *Checker) getMembersOfSymbol(symbol *ast.Symbol) ast.SymbolTable { func (c *Checker) getExportsOfModule(moduleSymbol *ast.Symbol) ast.SymbolTable { links := c.moduleSymbolLinks.Get(moduleSymbol) + key := memoKeyOf(links, memoModuleExports) + if links.resolvedExports != nil && c.readsWithoutMemo(key) { + return links.resolvedExports + } + c.beginMemo(key) + result := c.getExportsOfModuleCached(moduleSymbol, links) + c.endMemo() + return result +} + +func (c *Checker) getExportsOfModuleCached(moduleSymbol *ast.Symbol, links *ModuleSymbolLinks) ast.SymbolTable { if links.resolvedExports == nil { exports, typeOnlyExportStarMap := c.getExportsOfModuleWorker(moduleSymbol) - links.resolvedExports = exports - links.typeOnlyExportStarMap = typeOnlyExportStarMap + key := memoKeyOf(links, memoModuleExports) + c.setFlagMemoValue(key, &links.typeOnlyExportStarMap, typeOnlyExportStarMap) + c.setFlagMemoValue(key, &links.resolvedExports, exports) } return links.resolvedExports } @@ -16947,12 +17571,23 @@ func (c *Checker) ResolveAlias(symbol *ast.Symbol) (*ast.Symbol, bool) { // meaning. Pure aliases are eagerly resolved and any type-only markers are back-propagated to the original // symbol. The function panics if the argument is not a symbol with an alias meaning. func (c *Checker) resolveAlias(symbol *ast.Symbol) *ast.Symbol { + links := c.aliasSymbolLinks.Get(symbol) + if target := links.aliasTarget.get(c); target != nil { + return target + } + links.aliasTarget.begin(c) + result := c.resolveAliasUncached(symbol) + c.endMemo() + return result +} + +func (c *Checker) resolveAliasUncached(symbol *ast.Symbol) *ast.Symbol { if symbol.Flags()&ast.SymbolFlagsAlias == 0 { panic("Should only get alias here") } links := c.aliasSymbolLinks.Get(symbol) - if links.aliasTarget == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameAliasTarget) { + if links.aliasTarget.get(c) == nil { + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameAliasTarget, &links.aliasTarget.memoHeader) { return c.unknownSymbol } node := c.getDeclarationOfAliasSymbol(symbol) @@ -16964,13 +17599,13 @@ func (c *Checker) resolveAlias(symbol *ast.Symbol) *ast.Symbol { // When the target is a pure alias, we transitively resolve and propagate any typeOnlyDeclaration target = c.resolveIndirectionAlias(symbol, target) } - links.aliasTarget = core.OrElse(target, c.unknownSymbol) + links.aliasTarget.set(c, core.OrElse(target, c.unknownSymbol)) if !c.popTypeResolution() { c.error(node, diagnostics.Circular_definition_of_import_alias_0, c.symbolToString(symbol)) - links.aliasTarget = c.unknownSymbol + links.aliasTarget.set(c, c.unknownSymbol) } } - return links.aliasTarget + return links.aliasTarget.get(c) } func (c *Checker) resolveIndirectionAlias(source *ast.Symbol, target *ast.Symbol) *ast.Symbol { @@ -16985,7 +17620,7 @@ func (c *Checker) resolveIndirectionAlias(source *ast.Symbol, target *ast.Symbol func (c *Checker) tryResolveAlias(symbol *ast.Symbol) *ast.Symbol { links := c.aliasSymbolLinks.Get(symbol) - if links.aliasTarget != nil || c.findResolutionCycleStartIndex(symbol, TypeSystemPropertyNameAliasTarget) < 0 { + if links.aliasTarget.get(c) != nil || c.findResolutionCycleStartIndex(symbol, TypeSystemPropertyNameAliasTarget) < 0 { return c.resolveAlias(symbol) } return nil @@ -17083,20 +17718,20 @@ func (c *Checker) getDeclarationOfAliasSymbol(symbol *ast.Symbol) *ast.Node { func (c *Checker) getTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { deferred := c.deferredSymbolLinks.Get(symbol) if deferred.parent.flags&TypeFlagsUnion != 0 { - links.resolvedType = c.getUnionType(deferred.constituents) + links.resolvedType.set(c, c.getUnionType(deferred.constituents)) } else { - links.resolvedType = c.getIntersectionType(deferred.constituents) + links.resolvedType.set(c, c.getIntersectionType(deferred.constituents)) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getWriteTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.getWriteType() == nil { + if links.getWriteType(c) == nil { deferred := c.deferredSymbolLinks.Get(symbol) if len(deferred.writeConstituents) != 0 { if deferred.parent.flags&TypeFlagsUnion != 0 { @@ -17108,19 +17743,26 @@ func (c *Checker) getWriteTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type links.setWriteType(c, c.getTypeOfSymbolWithDeferredType(symbol)) } } - return links.getWriteType() + return links.getWriteType(c) } // Distinct write types come only from set accessors, but synthetic union and intersection // properties deriving from set accessors will either pre-compute or defer the union or // intersection of the writeTypes of their constituents. func (c *Checker) getWriteTypeOfSymbol(symbol *ast.Symbol) *Type { + c.beginMemo(memoKeyOf(symbol, memoWriteType)) + result := c.getWriteTypeOfSymbolUncached(symbol) + c.endMemo() + return result +} + +func (c *Checker) getWriteTypeOfSymbolUncached(symbol *ast.Symbol) *Type { if symbol.CheckFlags()&ast.CheckFlagsSyntheticProperty != 0 { if symbol.CheckFlags()&ast.CheckFlagsDeferredType != 0 { return c.getWriteTypeOfSymbolWithDeferredType(symbol) } links := c.valueSymbolLinks.Get(symbol) - return core.OrElse(links.getWriteType(), links.resolvedType) + return core.OrElse(links.getWriteType(c), links.resolvedType.get(c)) } if symbol.Flags()&ast.SymbolFlagsProperty != 0 { return c.removeMissingType(c.getTypeOfSymbol(symbol), symbol.Flags()&ast.SymbolFlagsOptional != 0) @@ -17178,7 +17820,7 @@ func (c *Checker) GetTypeOfSymbolAtLocation(symbol *ast.Symbol, location *ast.No // getSymbolOfNameOrPropertyAccessExpression), so an access that resolved to // none is matched against the index signature it would use. func (c *Checker) resolvedSymbolAtLocation(location *ast.Node, symbol *ast.Symbol) *ast.Symbol { - resolved := c.symbolNodeLinks.Get(location).resolvedSymbol + resolved := c.symbolNodeLinks.Get(location).resolvedSymbol.get(c) if resolved == nil && symbol.CheckFlags()&ast.CheckFlagsIndexSymbol != 0 && ast.IsPropertyAccessExpression(location) && !ast.IsPrivateIdentifier(location.Name()) { resolved = c.getApplicableIndexSymbol(c.checkExpressionCached(location.Expression()), c.getLiteralTypeFromPropertyName(location.Name())) } @@ -17186,6 +17828,17 @@ func (c *Checker) resolvedSymbolAtLocation(location *ast.Node, symbol *ast.Symbo } func (c *Checker) getTypeOfSymbol(symbol *ast.Symbol) *Type { + links := c.valueSymbolLinks.Get(symbol) + if t := links.resolvedType.get(c); t != nil { + return t + } + links.resolvedType.begin(c) + result := c.getTypeOfSymbolUncached(symbol) + c.endMemo() + return result +} + +func (c *Checker) getTypeOfSymbolUncached(symbol *ast.Symbol) *Type { if symbol.CheckFlags()&ast.CheckFlagsDeferredType != 0 { return c.getTypeOfSymbolWithDeferredType(symbol) } @@ -17222,23 +17875,23 @@ func (c *Checker) getNonMissingTypeOfSymbol(symbol *ast.Symbol) *Type { func (c *Checker) getTypeOfInstantiatedSymbol(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - links.resolvedType = c.instantiateType(c.getTypeOfSymbol(links.target), links.mapper) + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.instantiateType(c.getTypeOfSymbol(links.target), links.mapper)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getWriteTypeOfInstantiatedSymbol(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.getWriteType() == nil { + if links.getWriteType(c) == nil { links.setWriteType(c, c.instantiateType(c.getWriteTypeOfSymbol(links.target), links.mapper)) } - return links.getWriteType() + return links.getWriteType(c) } func (c *Checker) getTypeOfVariableOrParameterOrProperty(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { t := c.getTypeOfVariableOrParameterOrPropertyWorker(symbol) if t == nil { panic("Unexpected nil type") @@ -17248,15 +17901,15 @@ func (c *Checker) getTypeOfVariableOrParameterOrProperty(symbol *ast.Symbol) *Ty // to preserve this type. In fact, we need to _prefer_ that type, but it won't // be assigned until contextual typing is complete, so we need to defer in // cases where contextual typing may take place. - if assigned := links.resolvedType; assigned != nil { + if assigned := links.resolvedType.get(c); assigned != nil { return assigned } if !c.isParameterOfContextSensitiveSignature(symbol) { - links.resolvedType = t + links.resolvedType.set(c, t) } return t } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) isParameterOfContextSensitiveSignature(symbol *ast.Symbol) bool { @@ -17291,17 +17944,35 @@ func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbo } return c.getWidenedType(c.getWidenedLiteralType(c.checkExpression(statements[0].Expression()))) } + if ast.IsBindingElement(declaration) && c.strictNullChecks { + // The type of a binding element depends on the type of the + // initializer its pattern destructures (see + // getBindingElementTypeFromParentType), which may in turn depend on + // the element. Resolving the initializer first, as for any element of + // the pattern, makes the cycle resolve the same way whichever element + // is requested first. + if initializer := declaration.Parent.Parent.Initializer(); initializer != nil && !c.isResolutionInProgress(initializer, TypeSystemPropertyNamePatternInitializer) { + c.beginTypeResolution(initializer, TypeSystemPropertyNamePatternInitializer, nil) + c.getTypeOfInitializer(initializer) + c.popTypeResolution() + } + } // Handle variable, parameter or property - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType, &c.valueSymbolLinks.Get(symbol).resolvedType.memoHeader) { return c.reportCircularityError(symbol) } + var result *Type if symbol.Flags()&ast.SymbolFlagsModuleExports != 0 { if symbol.Name() == "exports" { - return c.getTypeOfSymbol(c.resolveExternalModuleSymbol(symbol.ValueDeclaration().Symbol(), false /*dontResolveAlias*/)) + result = c.getTypeOfSymbol(c.resolveExternalModuleSymbol(symbol.ValueDeclaration().Symbol(), false /*dontResolveAlias*/)) + } else { + result = c.newAnonymousType(symbol, symbol.Members(), nil, nil, nil) + } + if !c.popTypeResolution() { + return c.reportCircularityError(symbol) } - return c.newAnonymousType(symbol, symbol.Members(), nil, nil, nil) + return result } - var result *Type switch declaration.Kind { case ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindVariableDeclaration, ast.KindBindingElement: @@ -17540,11 +18211,11 @@ func (c *Checker) padObjectLiteralType(t *Type, pattern *ast.Node) *Type { name := c.getPropertyNameFromBindingElement(e) propType := c.getTypeFromBindingElement(e, false /*includePatternInType*/, true /*reportErrors*/) key := symbolWithTypeKey{name: name, t: propType} - symbol := c.paddedProperties[key] + symbol, _ := c.paddedProperties.get(key) if symbol == nil { symbol = c.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsOptional, name) - c.valueSymbolLinks.Get(symbol).resolvedType = propType - c.paddedProperties[key] = symbol + c.valueSymbolLinks.Get(symbol).resolvedType.setIdentity(c, propType) + c.paddedProperties.intern(key, symbol) } members.Set(symbol.Name(), symbol) } @@ -17612,8 +18283,8 @@ func (c *Checker) getWidenedLiteralTypeForInitializer(declaration *ast.Node, t * func (c *Checker) getTypeOfFuncClassEnumModule(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - links.resolvedType = c.getTypeOfFuncClassEnumModuleWorker(symbol) + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.getTypeOfFuncClassEnumModuleWorker(symbol)) } if declaration := symbol.ValueDeclaration(); declaration != nil && c.isContextSensitiveFunctionOrObjectLiteralMethod(declaration) { // Complete the signature before anything resolves the members of the @@ -17621,7 +18292,7 @@ func (c *Checker) getTypeOfFuncClassEnumModule(symbol *ast.Symbol) *Type { // members of the type. c.assignContextualParameterTypesOfFunction(declaration, CheckModeNormal) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeOfFuncClassEnumModuleWorker(symbol *ast.Symbol) *Type { @@ -17670,16 +18341,23 @@ func (c *Checker) getBaseTypeVariableOfClass(symbol *ast.Symbol) *Type { * * an object type with at least one construct signature. */ func (c *Checker) getBaseConstructorTypeOfClass(t *Type) *Type { + t.AsInterfaceType().resolvedBaseConstructorType.begin(c) + result := c.getBaseConstructorTypeOfClassUncached(t) + c.endMemo() + return result +} + +func (c *Checker) getBaseConstructorTypeOfClassUncached(t *Type) *Type { data := t.AsInterfaceType() - if data.resolvedBaseConstructorType != nil { - return data.resolvedBaseConstructorType + if data.resolvedBaseConstructorType.get(c) != nil { + return data.resolvedBaseConstructorType.get(c) } baseTypeNode := getBaseTypeNodeOfClass(t) if baseTypeNode == nil { - data.resolvedBaseConstructorType = c.undefinedType - return data.resolvedBaseConstructorType + data.resolvedBaseConstructorType.set(c, c.undefinedType) + return data.resolvedBaseConstructorType.get(c) } - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstructorType) { + if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstructorType, &data.resolvedBaseConstructorType.memoHeader) { return c.errorType } baseConstructorType := c.checkExpression(baseTypeNode.Expression()) @@ -17690,10 +18368,10 @@ func (c *Checker) getBaseConstructorTypeOfClass(t *Type) *Type { } if !c.popTypeResolution() { c.error(t.symbol.ValueDeclaration(), diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_base_expression, c.symbolToString(t.symbol)) - if data.resolvedBaseConstructorType == nil { - data.resolvedBaseConstructorType = c.errorType + if data.resolvedBaseConstructorType.get(c) == nil { + data.resolvedBaseConstructorType.set(c, c.errorType) } - return data.resolvedBaseConstructorType + return data.resolvedBaseConstructorType.get(c) } if baseConstructorType.flags&TypeFlagsAny == 0 && baseConstructorType != c.nullWideningType && !c.isConstructorType(baseConstructorType) { err := c.error(baseTypeNode.Expression(), diagnostics.Type_0_is_not_a_constructor_function_type, c.TypeToString(baseConstructorType)) @@ -17710,15 +18388,15 @@ func (c *Checker) getBaseConstructorTypeOfClass(t *Type) *Type { err.AddRelatedInfo(createDiagnosticForNode(baseConstructorType.symbol.Declarations()[0], diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, c.symbolToString(baseConstructorType.symbol), c.TypeToString(ctorReturn))) } } - if data.resolvedBaseConstructorType == nil { - data.resolvedBaseConstructorType = c.errorType + if data.resolvedBaseConstructorType.get(c) == nil { + data.resolvedBaseConstructorType.set(c, c.errorType) } - return data.resolvedBaseConstructorType + return data.resolvedBaseConstructorType.get(c) } - if data.resolvedBaseConstructorType == nil { - data.resolvedBaseConstructorType = baseConstructorType + if data.resolvedBaseConstructorType.get(c) == nil { + data.resolvedBaseConstructorType.set(c, baseConstructorType) } - return data.resolvedBaseConstructorType + return data.resolvedBaseConstructorType.get(c) } func (c *Checker) isFunctionType(t *Type) bool { @@ -17772,6 +18450,17 @@ func (c *Checker) getConstraintOfType(t *Type) *Type { } func (c *Checker) getConstraintOfTypeParameter(typeParameter *Type) *Type { + key := memoKeyOf(typeParameter, memoTypeParameterConstraint) + if typeParameter.flags&TypeFlagsTypeParameter != 0 && typeParameter.AsTypeParameter().resolvedBaseConstraint.peek() != nil && typeParameter.AsTypeParameter().constraint.peek() != nil && c.readsWithoutMemo(key) { + return c.getConstraintOfTypeParameterUncached(typeParameter) + } + c.beginMemo(key) + result := c.getConstraintOfTypeParameterUncached(typeParameter) + c.endMemo() + return result +} + +func (c *Checker) getConstraintOfTypeParameterUncached(typeParameter *Type) *Type { if c.hasNonCircularBaseConstraint(typeParameter) { return c.getConstraintFromTypeParameter(typeParameter) } @@ -17789,7 +18478,7 @@ func (c *Checker) getConstraintFromTypeParameter(t *Type) *Type { } tp := t.AsTypeParameter() - if tp.constraint == nil { + if tp.constraint.get(c) == nil { var constraint *Type if tp.target != nil { constraint = c.instantiateType(c.getConstraintOfTypeParameter(tp.target), tp.mapper) @@ -17813,10 +18502,10 @@ func (c *Checker) getConstraintFromTypeParameter(t *Type) *Type { if constraint == nil { constraint = c.noConstraintType } - tp.constraint = constraint + tp.constraint.set(c, constraint) } - if tp.constraint != c.noConstraintType { - return tp.constraint + if tp.constraint.get(c) != c.noConstraintType { + return tp.constraint.get(c) } return nil } @@ -17913,7 +18602,7 @@ func (c *Checker) getTypeParametersForTypeReferenceOrImport(node *ast.Node) []*T func (c *Checker) getTypeParametersForTypeAndSymbol(t *Type, symbol *ast.Symbol) []*Type { if !c.isErrorType(t) { if symbol.Flags()&ast.SymbolFlagsTypeAlias != 0 { - if typeParameters := c.typeAliasLinks.Get(symbol).typeParameters; len(typeParameters) != 0 { + if typeParameters := c.getTypeParametersOfTypeAlias(symbol); len(typeParameters) != 0 { return typeParameters } } @@ -17977,7 +18666,7 @@ func (c *Checker) getConstraintFromConditionalType(t *Type) *Type { func (c *Checker) getDefaultConstraintOfConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedDefaultConstraint == nil { + return c.memoCompute(&d.resolvedDefaultConstraint, func() *Type { // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, @@ -17986,19 +18675,18 @@ func (c *Checker) getDefaultConstraintOfConditionalType(t *Type) *Type { falseConstraint := c.getFalseTypeFromConditionalType(t) switch { case IsTypeAny(trueConstraint): - d.resolvedDefaultConstraint = falseConstraint + return falseConstraint case IsTypeAny(falseConstraint): - d.resolvedDefaultConstraint = trueConstraint + return trueConstraint default: - d.resolvedDefaultConstraint = c.getUnionType([]*Type{trueConstraint, falseConstraint}) + return c.getUnionType([]*Type{trueConstraint, falseConstraint}) } - } - return d.resolvedDefaultConstraint + }) } func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedConstraintOfDistributive == nil { + result := c.memoCompute(&d.resolvedConstraintOfDistributive, func() *Type { // Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained // type parameter. If so, create an instantiation of the conditional type where T is replaced // with its constraint. We do this because if the constraint is a union type it will be distributed @@ -18010,7 +18698,7 @@ func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter // a union - once negated types exist and are applied to the conditional false branch, this "constraint" // likely doesn't need to exist. - if d.root.isDistributive && c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id}] != t { + if d.root.isDistributive && !c.isRestrictiveInstantiation(t) { constraint := c.getSimplifiedType(d.checkType, false /*writing*/) if constraint == d.checkType { constraint = c.getConstraintOfType(constraint) @@ -18018,25 +18706,24 @@ func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { if constraint != nil && constraint != d.checkType { instantiated := c.getConditionalTypeInstantiation(t, prependTypeMapping(d.root.checkType, constraint, d.mapper), true /*forConstraint*/, nil) if instantiated.flags&TypeFlagsNever == 0 { - d.resolvedConstraintOfDistributive = instantiated return instantiated } } } - d.resolvedConstraintOfDistributive = c.noConstraintType - } - if d.resolvedConstraintOfDistributive != c.noConstraintType { - return d.resolvedConstraintOfDistributive + return c.noConstraintType + }) + if result != c.noConstraintType { + return result } return nil } func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type { links := c.declaredTypeLinks.Get(symbol) - if links.declaredType == nil { + if links.declaredType.get(c) == nil { kind := core.IfElse(symbol.Flags()&ast.SymbolFlagsClass != 0, ObjectFlagsClass, ObjectFlagsInterface) t := c.newObjectType(kind, symbol) - links.declaredType = t + links.declaredType.setIdentity(c, t) outerTypeParameters := c.getOuterTypeParametersOfClassOrInterface(symbol) typeParameters := c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(outerTypeParameters, symbol) // A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type @@ -18049,16 +18736,15 @@ func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type { d := t.AsInterfaceType() d.thisType = c.newTypeParameter(symbol) d.thisType.AsTypeParameter().isThisType = true - d.thisType.AsTypeParameter().constraint = t + d.thisType.AsTypeParameter().constraint.intern(t) d.allTypeParameters = append(typeParameters, d.thisType) d.outerTypeParameterCount = len(outerTypeParameters) - d.resolvedTypeArguments = d.TypeParameters() - d.instantiations = make(map[CacheHashKey]*Type) - d.instantiations[getTypeListKey(d.resolvedTypeArguments)] = t + d.resolvedTypeArguments.intern(d.TypeParameters()) + d.instantiations.intern(getTypeListKey(d.TypeParameters()), t) d.target = t } } - return links.declaredType + return links.declaredType.get(c) } /** @@ -18186,15 +18872,15 @@ func (b *keyBuilder) writeAlias(alias *TypeAlias) { } } -func (b *keyBuilder) writeGenericTypeReferences(source *Type, target *Type, ignoreConstraints bool) bool { +func (b *keyBuilder) writeGenericTypeReferences(c *Checker, source *Type, target *Type, ignoreConstraints bool) bool { var constrained bool typeParameters := make([]*Type, 0, 8) var writeTypeReference func(*Type, int) writeTypeReference = func(ref *Type, depth int) { b.writeType(ref.Target()) - for _, t := range ref.AsTypeReference().resolvedTypeArguments { + for _, t := range ref.AsTypeReference().resolvedTypeArguments.peek() { if t.flags&TypeFlagsTypeParameter != 0 { - if ignoreConstraints || t.checker.getConstraintOfTypeParameter(t) == nil { + if ignoreConstraints || c.getConstraintOfTypeParameter(t) == nil { index := slices.Index(typeParameters, t) if index < 0 { index = len(typeParameters) @@ -18205,7 +18891,7 @@ func (b *keyBuilder) writeGenericTypeReferences(source *Type, target *Type, igno continue } constrained = true - } else if depth < 4 && isTypeReferenceWithGenericArguments(t) { + } else if depth < 4 && c.isTypeReferenceWithGenericArguments(t) { b.writeByte('<') writeTypeReference(t, depth+1) b.writeByte('>') @@ -18231,52 +18917,49 @@ func (b *keyBuilder) writeNode(node *ast.Node) { } } -// getObjectLiteralTypeKey identifies the content of an object literal type, so -// that checking the same object literal again with the same results yields the -// same type. -func (c *Checker) getObjectLiteralTypeKey(node *ast.Node, flags ObjectFlags, properties ast.SymbolTable, indexInfos []*IndexInfo) CacheHashKey { - var b keyBuilder - b.writeNode(node) - b.writeUint32(uint32(flags)) - names := slices.Sorted(properties.Keys()) - b.writeInt(len(names)) - for _, name := range names { - prop := properties.Get(name) - b.writeString(name) - b.writeByte(0) - b.writeUint32(uint32(prop.Flags())) - b.writeUint32(uint32(prop.CheckFlags())) - // Members created by this object literal have their types set at +// An objectLiteralType is a type created for an object literal, with the +// content it was created from; see isSameObjectLiteralType. +type objectLiteralType struct { + flags ObjectFlags + properties ast.SymbolTable + indexInfos []*IndexInfo + t *Type +} + +// isSameObjectLiteralType reports whether an object literal type created +// before has the given content, so that checking the same object literal +// again with the same results yields the same type. +func (c *Checker) isSameObjectLiteralType(e objectLiteralType, flags ObjectFlags, properties ast.SymbolTable, indexInfos []*IndexInfo) bool { + if e.flags != flags || e.properties.Len() != properties.Len() || len(e.indexInfos) != len(indexInfos) { + return false + } + for i, info := range indexInfos { + other := e.indexInfos[i] + if info.keyType != other.keyType || info.valueType != other.valueType || info.isReadonly != other.isReadonly || !slices.Equal(info.components, other.components) { + return false + } + } + for i := range properties.Len() { + name, prop := properties.At(i) + otherName, other := e.properties.At(i) + if name != otherName || prop.Flags() != other.Flags() || prop.CheckFlags() != other.CheckFlags() { + return false + } + // Members created by an object literal have their types set at // creation; other members (accessors) are the declared symbols. links := c.valueSymbolLinks.TryGet(prop) - if links == nil || links.target == nil { - b.writeByte(2) - b.writeSymbol(prop) + otherLinks := c.valueSymbolLinks.TryGet(other) + if links == nil || links.target == nil || otherLinks == nil || otherLinks.target == nil { + if prop != other { + return false + } continue } - if nameType := links.getNameType(); nameType != nil { - b.writeByte(1) - b.writeType(nameType) - } else { - b.writeByte(0) - } - b.writeType(links.resolvedType) - } - b.writeInt(len(indexInfos)) - for _, info := range indexInfos { - b.writeType(info.keyType) - b.writeType(info.valueType) - if info.isReadonly { - b.writeByte(1) - } else { - b.writeByte(0) - } - b.writeInt(len(info.components)) - for _, n := range info.components { - b.writeNode(n) + if links.getNameType() != otherLinks.getNameType() || links.resolvedType.peek() != otherLinks.resolvedType.peek() { + return false } } - return b.hash() + return true } func getTypeListKey(types []*Type) CacheHashKey { @@ -18396,15 +19079,22 @@ func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstrain return b.hash() } -func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) (CacheHashKey, bool) { +func (c *Checker) getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) (CacheHashKey, bool) { if isIdentity && source.id > target.id { source, target = target, source } var b keyBuilder var constrained bool - if isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) { - b.writeByte('g') - constrained = b.writeGenericTypeReferences(source, target, ignoreConstraints) + if c.isTypeReferenceWithGenericArguments(source) && c.isTypeReferenceWithGenericArguments(target) { + // Marker types are compared structurally while their variances are being + // measured, and other instantiations with the same key by those variances, + // which may be guesses until the measurement finishes. + if c.isMarkerType(source) || c.isMarkerType(target) { + b.writeByte('m') + } else { + b.writeByte('g') + } + constrained = b.writeGenericTypeReferences(c, source, target, ignoreConstraints) } else { b.writeByte('s') b.writeType(source) @@ -18423,9 +19113,9 @@ func getNodeListKey(nodes []*ast.Node) CacheHashKey { return b.hash() } -func isTypeReferenceWithGenericArguments(t *Type) bool { - return isNonDeferredTypeReference(t) && core.Some(t.checker.getTypeArguments(t), func(t *Type) bool { - return t.flags&TypeFlagsTypeParameter != 0 || isTypeReferenceWithGenericArguments(t) +func (c *Checker) isTypeReferenceWithGenericArguments(t *Type) bool { + return isNonDeferredTypeReference(t) && core.Some(c.getTypeArguments(t), func(t *Type) bool { + return t.flags&TypeFlagsTypeParameter != 0 || c.isTypeReferenceWithGenericArguments(t) }) } @@ -18692,7 +19382,10 @@ func (c *Checker) getParentElementAccess(node *ast.Node) *ast.Node { // the parameter. func (c *Checker) getTypeFromBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type { if includePatternInType { - c.contextualBindingPatterns = append(c.contextualBindingPatterns, pattern) + // While the implied type is computed, references to the pattern's own + // elements are any (see checkIdentifier), so nothing computed from + // them may be cached; see isResolvingAnywhere. + c.beginTypeResolution(pattern, TypeSystemPropertyNameContextualBindingPattern, nil) } var result *Type if ast.IsObjectBindingPattern(pattern) { @@ -18701,7 +19394,7 @@ func (c *Checker) getTypeFromBindingPattern(pattern *ast.Node, includePatternInT result = c.getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors) } if includePatternInType { - c.contextualBindingPatterns = c.contextualBindingPatterns[:len(c.contextualBindingPatterns)-1] + c.popTypeResolution() } return result } @@ -18726,7 +19419,7 @@ func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatt text := getPropertyNameFromType(exprType) flags := ast.SymbolFlagsProperty | core.IfElse(e.Initializer() != nil, ast.SymbolFlagsOptional, 0) symbol := c.newSymbol(flags, text) - c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, includePatternInType, reportErrors) + c.valueSymbolLinks.Get(symbol).resolvedType.setIdentity(c, c.getTypeFromBindingElement(e, includePatternInType, reportErrors)) members.Set(symbol.Name(), symbol) } var indexInfos []*IndexInfo @@ -18744,24 +19437,23 @@ func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatt } b.writeUint32(uint32(objectFlags)) b.writeInt(len(indexInfos)) - names := slices.Sorted(members.Keys()) - for _, name := range names { + for name, member := range members.All() { b.writeString(name) b.writeByte(0) - b.writeUint32(uint32(members.Get(name).Flags())) - b.writeType(c.valueSymbolLinks.Get(members.Get(name)).resolvedType) + b.writeUint32(uint32(member.Flags())) + b.writeType(c.valueSymbolLinks.Get(member).resolvedType.peek()) } key := b.hash() - if existing := c.bindingPatternTypes[key]; existing != nil { + if existing, _ := c.bindingPatternTypes.get(key); existing != nil { return existing } result := c.newAnonymousType(nil, members, nil, nil, indexInfos) result.objectFlags |= objectFlags if includePatternInType { - c.patternForType[result] = pattern + c.patternForType.intern(result, pattern) result.objectFlags |= ObjectFlagsContainsObjectOrArrayLiteral } - c.bindingPatternTypes[key] = result + c.bindingPatternTypes.intern(key, result) return result } @@ -18809,13 +19501,13 @@ func (c *Checker) getTypeFromArrayBindingPattern(pattern *ast.Node, includePatte b.writeNode(pattern) b.writeType(result) key := b.hash() - if existing := c.bindingPatternTypes[key]; existing != nil { + if existing, _ := c.bindingPatternTypes.get(key); existing != nil { return existing } result = c.cloneTypeReference(result) - c.patternForType[result] = pattern + c.patternForType.intern(result, pattern) result.objectFlags |= ObjectFlagsContainsObjectOrArrayLiteral - c.bindingPatternTypes[key] = result + c.bindingPatternTypes.intern(key, result) } return result } @@ -19008,16 +19700,17 @@ func (c *Checker) getTypeFromPropertyDescriptor(node *ast.Node) *Type { // A property is considered a constructor declared property when all declaration sites are this.xxx assignments, // when no declaration sites have JSDoc type annotations, and when at least one declaration site is in the body of // a class constructor. +type thisExpando struct { + kind thisAssignmentDeclarationKind + location *ast.Node +} + func (c *Checker) isConstructorDeclaredThisProperty(symbol *ast.Symbol) (thisAssignmentDeclarationKind, *ast.Node) { if symbol.ValueDeclaration() == nil || !ast.IsBinaryExpression(symbol.ValueDeclaration()) { return thisAssignmentDeclarationNone, nil } - if kind, ok := c.thisExpandoKinds[symbol]; ok { - location, ok2 := c.thisExpandoLocations[symbol] - if !ok2 { - panic("location should be cached whenever this expando symbol is cached") - } - return kind, location + if cached, ok := c.thisExpandos.get(symbol); ok { + return cached.kind, cached.location } allThis := true var typeAnnotation *ast.Node @@ -19048,8 +19741,7 @@ func (c *Checker) isConstructorDeclaredThisProperty(symbol *ast.Symbol) (thisAss kind = core.IfElse(location == nil, thisAssignmentDeclarationMethod, thisAssignmentDeclarationConstructor) } } - c.thisExpandoKinds[symbol] = kind - c.thisExpandoLocations[symbol] = location + c.thisExpandos.intern(symbol, thisExpando{kind, location}) return kind, location } @@ -19183,9 +19875,12 @@ func (c *Checker) getWidenedType(t *Type) *Type { func (c *Checker) getWidenedTypeWithContext(t *Type, context *WideningContext) *Type { if t.objectFlags&ObjectFlagsRequiresWidening != 0 { if context == nil { - if cached := c.cachedTypes[CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}]; cached != nil { + key := CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id} + if cached, _ := c.cachedTypes.get(c, key); cached != nil { return cached } + c.cachedTypes.begin(c, key) + defer c.endMemo() } var result *Type switch { @@ -19214,7 +19909,7 @@ func (c *Checker) getWidenedTypeWithContext(t *Type, context *WideningContext) * result = c.createTypeReference(t.Target(), core.SameMap(c.getTypeArguments(t), c.getWidenedType)) } if result != nil && context == nil { - c.cachedTypes[CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}] = result + c.cachedTypes.set(c, CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}, result) } return core.OrElse(result, t) } @@ -19320,27 +20015,36 @@ func (c *Checker) getSiblingsOfContext(context *WideningContext) []*Type { } func (c *Checker) getUndefinedProperty(prop *ast.Symbol) *ast.Symbol { - if cached := c.undefinedProperties[prop]; cached != nil { + if cached, _ := c.undefinedProperties.get(c, prop); cached != nil { return cached } + c.undefinedProperties.begin(c, prop) result := c.createSymbolWithType(prop, c.undefinedOrMissingType) result.SetFlags(result.Flags() | ast.SymbolFlagsOptional) - c.undefinedProperties[prop] = result + c.undefinedProperties.set(c, prop, result) + c.endMemo() return result } func (c *Checker) getTypeOfEnumMember(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - links.resolvedType = c.getDeclaredTypeOfEnumMember(symbol) + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.getDeclaredTypeOfEnumMember(symbol)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeOfAccessors(symbol *ast.Symbol) *Type { + c.beginMemo(memoKeyOf(symbol, memoAccessorType)) + result := c.getTypeOfAccessorsUncached(symbol) + c.endMemo() + return result +} + +func (c *Checker) getTypeOfAccessorsUncached(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { + if links.resolvedType.get(c) == nil { + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType, &links.resolvedType.memoHeader) { return c.errorType } getter := ast.GetDeclarationOfKind(symbol, ast.KindGetAccessor) @@ -19385,17 +20089,20 @@ func (c *Checker) getTypeOfAccessors(symbol *ast.Symbol) *Type { } t = c.anyType } - if links.resolvedType == nil { - links.resolvedType = t + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, t) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getWriteTypeOfAccessors(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.getWriteType() == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameWriteType) { + if links.getWriteType(c) == nil { + if links.extra == nil { + links.extra = c.valueSymbolExtraArena.New() + } + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameWriteType, &links.extra.writeType.memoHeader) { return c.errorType } setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor) @@ -19413,7 +20120,7 @@ func (c *Checker) getWriteTypeOfAccessors(symbol *ast.Symbol) *Type { writeType = c.anyType } // Absent an explicit setter type annotation we use the read type of the accessor. - if links.getWriteType() == nil { + if links.getWriteType(c) == nil { if writeType != nil { links.setWriteType(c, writeType) } else { @@ -19421,13 +20128,13 @@ func (c *Checker) getWriteTypeOfAccessors(symbol *ast.Symbol) *Type { } } } - return links.getWriteType() + return links.getWriteType(c) } func (c *Checker) getTypeOfAlias(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { + if links.resolvedType.get(c) == nil { + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType, &links.resolvedType.memoHeader) { return c.errorType } targetSymbol := c.resolveAlias(symbol) @@ -19437,22 +20144,22 @@ func (c *Checker) getTypeOfAlias(symbol *ast.Symbol) *Type { // type symbol, call getDeclaredTypeOfSymbol. // This check is important because without it, a call to getTypeOfSymbol could end // up recursively calling getTypeOfAlias, causing a stack overflow. - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { if c.getSymbolFlags(targetSymbol)&ast.SymbolFlagsValue != 0 { - links.resolvedType = c.getTypeOfSymbol(targetSymbol) + links.resolvedType.set(c, c.getTypeOfSymbol(targetSymbol)) } else { - links.resolvedType = c.errorType + links.resolvedType.set(c, c.errorType) } } if !c.popTypeResolution() { c.reportCircularityError(core.OrElse(exportSymbol, symbol)) - if links.resolvedType == nil { - links.resolvedType = c.errorType + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.errorType) } - return links.resolvedType + return links.resolvedType.get(c) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) addOptionality(t *Type) *Type { @@ -19537,12 +20244,13 @@ func (c *Checker) getEffectivePropertyNameForPropertyNameNode(node *ast.Property case ast.IsComputedPropertyName(node): // This is cached so `getTypeOfExpression` isn't constantly reinvoked for every property name lookup links := c.computedNameLinks.Get(node) - if links != nil && !links.hasName.IsUnknown() { - return links.name, links.hasName.IsTrue() + if result := links.name.get(c); !result.exists.IsUnknown() { + return result.name, result.exists.IsTrue() } + links.name.begin(c) name, exists := c.tryGetNameFromType(c.getTypeOfExpression(node.Expression())) - links.name = name - links.hasName = core.BoolToTristate(exists) + links.name.set(c, computedName{name: name, exists: core.BoolToTristate(exists)}) + c.endMemo() return name, exists } return "", false @@ -19583,36 +20291,649 @@ func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.ModifierFla * * @param target The symbol, type, or signature whose type is being queried * @param propertyName The property name that should be used to query the target for its type + * @param memo The memo that caches the result of the resolution, or nil */ -func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { +func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName, memo *memoHeader) bool { resolutionCycleStartIndex := c.findResolutionCycleStartIndex(target, propertyName) if resolutionCycleStartIndex >= 0 { - // A cycle was found - for i := resolutionCycleStartIndex; i < len(c.typeResolutions); i++ { - c.typeResolutions[i].result = false + if !c.yieldCycleToMembers(resolutionCycleStartIndex) { + return false + } + } + c.beginTypeResolution(target, propertyName, memo) + if resolutionCycleStartIndex < 0 && c.resolutionStart > 0 { + if i := c.findHiddenResolution(target, propertyName); i >= 0 { + c.resolutions[len(c.resolutions)-1].typeResolution.outerSelf = int32(i + 1) + } + } + return true +} + +// findHiddenResolution returns the index in resolutions of the outermost +// resolution of the property of target in progress below resolutionStart, +// or -1. +func (c *Checker) findHiddenResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName) int { + found := -1 + for i := int(c.resolutions[len(c.resolutions)-1].prevTypeResolution) - 1; i >= 0; i = int(c.resolutions[i].prevTypeResolution) - 1 { + if i < c.resolutionStart { + if r := &c.resolutions[i].typeResolution; r.target == target && r.propertyName == propertyName { + found = i + } + } + } + return found +} + +// cycleYield is a canonical recomputation in progress; see yieldCycle. +type cycleYield struct { + end int // Index in resolutions of the resolution whose pop ends it, or -1 + target TypeSystemEntity + propertyName TypeSystemPropertyName + savedResolutionStart int + savedVisibleStart int + savedVisibleSigs int + savedDepths [3]depthLimit + savedReverseMapped reverseMappedState + savedContextual []ContextualInfo + savedInference []InferenceContextInfo + shadowed []shadowedResolving +} + +type reverseMappedState struct { + sourceStack []*Type + targetStack []*Type + expandingFlags ExpandingFlags + start int32 +} + +type shadowedResolving struct { + key memoKey + index int32 +} + +// yieldCycle handles re-entering the resolution in progress at start, a +// cycle that would fail the resolution re-entered. Which resolution is +// re-entered depends on which was entered first, so a cycle through members +// declared before the one re-entered (see compareCycleMembers) fails at the +// first of those instead, as if it had been resolved first: it is computed +// again from scratch, with nothing in progress visible, and the resolution +// re-entered is then computed again likewise, by the caller, which pushes it. +// The computations in progress then continue from those results. +// Termination: a resolution is never computed again while it is already +// being computed again. +func (c *Checker) yieldCycle(start int) bool { + r := c.resolutions[start].typeResolution + if c.isYieldingTo(r.target, r.propertyName) { + return false + } + first := c.firstCycleMember(start) + if first == nil { + return false + } + c.resolveCycleMember(*first) + c.beginCycleYield(r.target, r.propertyName, len(c.resolutions), len(c.resolutions)) + return true +} + +// yieldObservedCycle handles observing the resolution in progress at start +// without failing it, as yieldCycle handles re-entering it: if members of +// the cycle are declared before the resolution observed, the first of them +// is computed again from scratch, as if it had been resolved first, so that +// the caller can use its result. +func (c *Checker) yieldObservedCycle(start int) { + if r := &c.resolutions[start].typeResolution; c.isYieldingTo(r.target, r.propertyName) { + return + } + if first := c.firstCycleMember(start); first != nil { + c.resolveCycleMember(*first) + } +} + +// firstCycleMember returns the member of the cycle from the resolution at +// start to the top of the stack that is resolved first, if it is not the +// resolution at start, and is not already being computed again. +func (c *Checker) firstCycleMember(start int) *TypeResolution { + entered := &c.resolutions[start].typeResolution + var first *TypeResolution + if isCycleMember(entered) { + first = entered + } + for i := start + 1; i < len(c.resolutions); i++ { + if f := &c.resolutions[i]; f.isTypeResolution && isCycleMember(&f.typeResolution) && (first == nil || c.compareCycleMembers(&f.typeResolution, first) < 0) { + first = &f.typeResolution + } + } + if first == nil || first == entered || c.isYieldingTo(first.target, first.propertyName) { + return nil + } + return first +} + +// isCycleMember reports whether a type resolution may be resolved first in +// place of the one a cycle re-entered: the type of a symbol, or the return +// type of a declared signature. +func isCycleMember(r *TypeResolution) bool { + switch r.propertyName { + case TypeSystemPropertyNameType: + return true + case TypeSystemPropertyNameResolvedReturnType: + sig := r.target.(*Signature) + return sig.target == nil && sig.getComposite() == nil && sig.declaration != nil + } + return false +} + +// compareCycleMembers orders the members of a cycle in program order of +// their declarations, symbols first among members with the same one. +func (c *Checker) compareCycleMembers(a, b *TypeResolution) int { + aSymbol, aIsSymbol := a.target.(*ast.Symbol) + bSymbol, bIsSymbol := b.target.(*ast.Symbol) + switch { + case aIsSymbol && bIsSymbol: + return c.compareSymbols(aSymbol, bSymbol) + case aIsSymbol: + return c.compareSymbolToNode(aSymbol, b.target.(*Signature).declaration) + case bIsSymbol: + return -c.compareSymbolToNode(bSymbol, a.target.(*Signature).declaration) + } + return c.compareNodes(a.target.(*Signature).declaration, b.target.(*Signature).declaration) +} + +// compareSymbolToNode orders a symbol by its first declaration against node, +// the symbol first if that is node. +func (c *Checker) compareSymbolToNode(s *ast.Symbol, node *ast.Node) int { + if len(s.Declarations()) == 0 { + return 1 + } + if r := c.compareNodes(s.Declarations()[0], node); r != 0 { + return r + } + return -1 +} + +// resolveCycleMember computes the result of a member of a cycle again from +// scratch, with nothing in progress visible. +func (c *Checker) resolveCycleMember(r TypeResolution) { + target, propertyName := r.target, r.propertyName + c.beginCycleYield(target, propertyName, -1, len(c.resolutions)) + switch propertyName { + case TypeSystemPropertyNameType: + c.getTypeOfSymbol(target.(*ast.Symbol)) + case TypeSystemPropertyNameResolvedReturnType: + c.getReturnTypeOfSignature(target.(*Signature)) + } + c.endCycleYield() +} + +// yieldCycleToMembers handles re-entering the type resolution in progress +// at start, a cycle that would fail it, when the cycle passes through a +// member table. Entering the cycle at the member table instead would not +// fail, as the table is available while it is resolved, so the resolution +// re-entered is computed again as if the table had been entered first: by +// the caller, with everything entered before the table hidden. A module +// table is instead resolved again itself, as if entered first (see +// resolveModuleTableMembers), so everything above it fails until then, to +// be discarded. Otherwise it yields as yieldCycle does. It reports whether +// the resolution re-entered may be pushed. +func (c *Checker) yieldCycleToMembers(start int) bool { + if m := c.findMembersInCycle(start); m >= 0 { + r := &c.resolutions[start].typeResolution + if c.isYieldingTo(r.target, r.propertyName) { + c.markResolutionCycle(start) + return false + } + if t := &c.resolutions[m].typeResolution; r.propertyName == TypeSystemPropertyNameResolvedTypeArguments && t.target == r.target { + // The members of a type reference are available only once its type + // arguments are resolved, so entering the cycle at them fails too. + c.markResolutionCycle(start) + return false + } + if t := &c.resolutions[m].typeResolution; t.propertyName == TypeSystemPropertyNameStructuredMembers && c.isModuleTableType(t.target.(*Type)) { + t.restartHidden = true + c.markResolutionCycle(m) + return false + } + c.beginCycleYield(r.target, r.propertyName, len(c.resolutions), m) + return true + } + if !c.yieldCycle(start) { + c.markResolutionCycle(start) + return false + } + return true +} + +// findMembersInCycle returns the index in resolutions of the outermost +// resolution of a member table in progress above start, or -1. Member +// tables are available while they are resolved, so a cycle entered at its +// member table never fails. +func (c *Checker) findMembersInCycle(start int) int { + for i := start + 1; i < len(c.resolutions); i++ { + f := &c.resolutions[i] + if f.isTypeResolution && f.typeResolution.propertyName == TypeSystemPropertyNameStructuredMembers { + return i + } + if f.memberSymbol != nil { + return i + } + } + return -1 +} + +func (c *Checker) isYieldingTo(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { + return slices.ContainsFunc(c.cycleYields, func(y cycleYield) bool { return y.target == target && y.propertyName == propertyName }) +} + +// beginCycleYield hides the resolutions in progress below floor until +// endCycleYield, which popping the resolution at end calls if it is not -1. +func (c *Checker) beginCycleYield(target TypeSystemEntity, propertyName TypeSystemPropertyName, end int, floor int) { + n := floor + c.cycleYields = append(c.cycleYields, cycleYield{ + end: end, + target: target, + propertyName: propertyName, + savedResolutionStart: c.resolutionStart, + savedVisibleStart: c.visibleStart, + savedVisibleSigs: c.visibleSignatures, + savedDepths: [3]depthLimit{c.serializationLevel, c.inlineLevel, c.conditionalConstraintDepth}, + savedReverseMapped: reverseMappedState{c.reverseMappedSourceStack, c.reverseMappedTargetStack, c.reverseExpandingFlags, c.reverseMappedStart}, + savedContextual: c.contextualInfos, + savedInference: c.inferenceContextInfos, + }) + // The contexts the hidden resolutions entered are hidden too, so that the + // recomputation is from scratch. + c.reverseMappedSourceStack, c.reverseMappedTargetStack, c.reverseExpandingFlags, c.reverseMappedStart = nil, nil, 0, 0 + c.contextualInfos, c.inferenceContextInfos = nil, nil + c.resolutionStart = n + c.visibleStart = n + c.visibleSignatures = len(c.signaturesResolving) + c.serializationLevel, c.inlineLevel, c.conditionalConstraintDepth = depthLimit{}, depthLimit{}, depthLimit{} +} + +func (c *Checker) endCycleYield() { + y := &c.cycleYields[len(c.cycleYields)-1] + c.resolutionStart = y.savedResolutionStart + c.visibleStart = y.savedVisibleStart + c.visibleSignatures = y.savedVisibleSigs + c.serializationLevel, c.inlineLevel, c.conditionalConstraintDepth = y.savedDepths[0], y.savedDepths[1], y.savedDepths[2] + r := y.savedReverseMapped + c.reverseMappedSourceStack, c.reverseMappedTargetStack, c.reverseExpandingFlags, c.reverseMappedStart = r.sourceStack, r.targetStack, r.expandingFlags, r.start + c.contextualInfos, c.inferenceContextInfos = y.savedContextual, y.savedInference + for _, sh := range y.shadowed { + c.resolving.set(sh.key, sh.index) + } + c.cycleYields[len(c.cycleYields)-1] = cycleYield{} + c.cycleYields = c.cycleYields[:len(c.cycleYields)-1] +} + +// markResolutionCycle records that the type resolution entries from start to +// the top of the stack form a cycle, so each of them fails. The failed +// results are kept, except that of a resolution yieldCycleToMembers computes +// again while it is in progress below: the computation below sets its own. +func (c *Checker) markResolutionCycle(start int) { + for i := start; i < len(c.resolutions); i++ { + if r := &c.resolutions[i].typeResolution; c.resolutions[i].isTypeResolution && r.result { + if !c.isRecomputedByYield(i) && r.memo != nil { + c.cycleParticipants = append(c.cycleParticipants, r.memo) + } + r.result = false + } + } + c.markTypeResolutionCircular(start) +} + +func (c *Checker) isRecomputedByYield(i int) bool { + for j := range c.cycleYields { + if c.cycleYields[j].end == i { + return true + } + } + return false +} + +// observeResolutionInProgress is for computations that are skipped rather +// than failed when re-entered. Skipping still observes the incomplete +// computation, so everything on the stack since it started is in a cycle. +func (c *Checker) observeResolutionInProgress(target TypeSystemEntity, propertyName TypeSystemPropertyName) { + if start := c.findResolutionCycleStartIndex(target, propertyName); start >= 0 { + c.markResolutionCycle(start) + } +} + +// isResolutionInProgress reports whether a resolution is on the stack, for +// computations that deliberately take another path to avoid a circularity. +// The answer depends on the incomplete resolution, so results cached while it +// is in progress must not outlive it, but nothing fails. +func (c *Checker) isResolutionInProgress(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { + start := c.findResolutionCycleStartIndex(target, propertyName) + if start < 0 { + return false + } + c.markTypeResolutionCircular(start) + return true +} + +// isResolvingAnywhere reports whether the property of target is being +// resolved. A computation finding it so fails, as computing it from scratch +// would, so the results cached since it started are discarded when it +// finishes, like those of any other failure, and every caller computes them +// again and reports the failure. Unlike isResolutionInProgress, it searches +// the whole stack, for computations that would not otherwise stop. +func (c *Checker) isResolvingAnywhere(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { + if i := c.resolvingAnywhereIndex(target, propertyName); i >= 0 { + c.markTypeResolutionCircular(i) + return true + } + return false +} + +// resolvingAnywhereIndex returns the index in resolutions of the visible +// resolution of the property of target in progress, or -1. +func (c *Checker) resolvingAnywhereIndex(target TypeSystemEntity, propertyName TypeSystemPropertyName) int { + for i := c.typeResolutionTop - 1; i >= c.visibleStart; i = int(c.resolutions[i].prevTypeResolution) - 1 { + if r := &c.resolutions[i].typeResolution; r.target == target && r.propertyName == propertyName { + return i + } + } + return -1 +} + +// observeCircularHead is for computations where re-entering one in progress +// fails only that computation, while the others complete from its failed +// result. Which member fails then depends on which was entered first, so the +// cycle records its canonical member, the first in program order, and the +// head resolves that member first when it finishes; see popCircularHead. +// Everything else cached during the cycle is discarded like for +// isResolutionInProgress. It returns the index in resolutions of the head, or +// -1 if target is not in progress. +func (c *Checker) observeCircularHead(target TypeSystemEntity, propertyName TypeSystemPropertyName, compare func(a, b TypeSystemEntity) int) int { + start := c.findResolutionCycleStartIndex(target, propertyName) + if start < 0 { + return -1 + } + c.markTypeResolutionCircular(start) + head := &c.resolutions[start].typeResolution + canonical := core.OrElse(head.canonical, head.target) + for i := start + 1; i < len(c.resolutions); i++ { + if r := &c.resolutions[i].typeResolution; c.resolutions[i].isTypeResolution && r.propertyName == propertyName && compare(r.target, canonical) < 0 { + canonical = r.target + } + } + head.canonical = canonical + return start +} + +// addCycleParticipants keeps the results of the type resolutions from start +// to the top of the stack when the cycle in progress ends, as they would be +// kept had the resolution at start been entered first. +func (c *Checker) addCycleParticipants(start int) { + for i := start; i < len(c.resolutions); i++ { + if r := &c.resolutions[i].typeResolution; c.resolutions[i].isTypeResolution && r.result && !r.participant && r.memo != nil { + r.participant = true + c.cycleParticipants = append(c.cycleParticipants, r.memo) } + } +} + +// popCircularHead pops a resolution that may head a cycle observed by +// observeCircularHead. It returns the cycle's canonical member when that is +// not the head itself, in which case everything cached since mark has been +// discarded, including the results of the cycle's participants, and the +// caller must resolve the canonical member before resolving the head again. +func (c *Checker) popCircularHead(mark discardMark) TypeSystemEntity { + r := c.topTypeResolution() + canonical := r.canonical + if canonical == r.target { + canonical = nil + } + if canonical != nil { + // Discard while the cycle is still in progress, so that the results + // of its participants are discarded too. + c.discardSince(mark) + } + c.popTypeResolution() + return canonical +} + +// enterSharedCurrentNode is enterCurrentNode for a node that shares the +// resolution of the enclosing node, small enough to inline for the common +// case. It reports false, changing nothing, if the node needs its own. +func (c *Checker) enterSharedCurrentNode(node *ast.Node) (savedCurrentNode, bool) { + save := savedCurrentNode{node: c.currentNode, resolution: c.currentNodeResolution} + if c.currentNodeResolution == 0 || c.currentNodeResolution != len(c.resolutions) || c.circularityStart != 0 || len(c.innerCycles) != 0 { + return save, false + } + c.currentNode = node + return save, true +} + +// exitSharedCurrentNode is exitCurrentNode for a node entered without a +// resolution of its own, when that ends no cycle. It reports false, changing +// nothing, otherwise. +func (c *Checker) exitSharedCurrentNode(save savedCurrentNode) bool { + if c.currentNodeResolution != save.resolution || c.circularityStart >= save.resolution || len(c.innerCycles) != 0 { return false } - c.typeResolutions = append(c.typeResolutions, TypeResolution{target: target, propertyName: propertyName, result: true}) + c.currentNode = save.node return true } +// enterCurrentNode makes node the node being checked, until the matching +// exitCurrentNode. Its resolution delimits the results that errors located at +// node (see reportAtCurrentNode) depend on. +func (c *Checker) enterCurrentNode(node *ast.Node) savedCurrentNode { + save := savedCurrentNode{node: c.currentNode, resolution: c.currentNodeResolution} + if c.circularityStart != 0 && c.circularityStart == c.currentNodeResolution && c.typeResolutionTop < c.currentNodeResolution { + // The results discarded when the current node is left depend on errors + // located at it, so a node within it must not reuse them either. + c.endCircularity() + } + c.currentNode = node + if c.currentNodeResolution != 0 && c.currentNodeResolution == len(c.resolutions) && c.circularityStart == 0 && len(c.innerCycles) == 0 { + // Nothing separates the node from the one enclosing it, so the + // resolution of that one stands in for it: a cycle through the node + // starts at that resolution, and nothing else can until the node is + // left, where exitCurrentNode ends it as popping the node's own + // resolution would. + return save + } + // The node's resolution shares the work of the one below it, as a type + // resolution does. + f := c.pushResolution() + parent := &c.resolutions[len(c.resolutions)-2] + f.depthStart = parent.depthStart + f.serial = parent.serial + c.currentNodeResolution = len(c.resolutions) + return save +} + +func (c *Checker) exitCurrentNode(save savedCurrentNode) { + if c.currentNodeResolution == save.resolution { + // The node had no resolution of its own; see enterCurrentNode. + c.endCyclesAt(save.resolution - 1) + c.currentNode = save.node + return + } + debug.Assert(c.currentNodeResolution == len(c.resolutions), "resolutions left on the stack") + c.dropResolution(c.currentNodeResolution - 1) + c.currentNode = save.node + c.currentNodeResolution = save.resolution +} + +// enterQuery starts a query made outside of checking, unless one is already +// in progress. Results that depend on errors located at the current node are +// discarded when the query finishes (see reportAtCurrentNode), so a query +// that reads results back from caches after computing them must do so before +// then. +func (c *Checker) enterQuery() (savedCurrentNode, bool) { + if c.memoTop != 0 || c.currentNodeResolution != 0 { + return savedCurrentNode{}, false + } + return c.enterCurrentNode(c.currentNode), true +} + +func (c *Checker) exitQuery(save savedCurrentNode, ok bool) { + if ok { + c.exitCurrentNode(save) + } +} + +type savedCurrentNode struct { + node *ast.Node + resolution int +} + +// markCurrentNodeCircular records that results cached until the current node +// is left depend on that node. +func (c *Checker) markCurrentNodeCircular() { + if c.currentNodeResolution != 0 { + c.extendCircularity(c.currentNodeResolution) + } +} + +// markTypeResolutionCircular records that the computations from start to the +// top of the stack observed the incomplete resolution at start without +// failing, so results cached while it is in progress are discarded when it +// finishes. +func (c *Checker) markTypeResolutionCircular(start int) { + c.recordFromCheck() + if !c.checking { + c.memoCycle(int(c.resolutions[start].prevMemo)) + } + c.markCircular(start) +} + +// keepsResultInCycle reports whether the type resolution at index n, which +// observed the cycles whose lowest head is observed (see Checker.observedHead), +// computes what it computes first from scratch although a cycle is in +// progress, so that its result is kept as a participant of the cycle: +// - The resolution re-entered a hidden resolution of itself, which a +// computation from scratch of the outer one always does, and what it +// observed in progress was all entered by the outer one, so its result is +// what the outer one computes first from scratch. +// - The resolution is the one a cycle yield computes again from scratch, +// with everything below it hidden, and it observed nothing hidden. +func (c *Checker) keepsResultInCycle(n int, f *resolution, observed int32) bool { + r := &f.typeResolution + if r.outerSelf != 0 && observed != 0 && observed >= r.outerSelf && int(observed) <= n { + return true + } + if len(c.cycleYields) != 0 { + // The innermost yield hides what is below visibleStart. + if floor := c.visibleStart; n >= floor && int(f.prevTypeResolution) <= floor && (observed == 0 || int(observed) > floor) { + return true + } + } + return false +} + +func minObservedHead(a int32, b int32) int32 { + if a == 0 || b != 0 && b < a { + return b + } + return a +} + +// markCircular records that the computations from the resolution at start to +// the top of the stack observed the incomplete computation at start, so +// results cached while it is in progress are discarded when it finishes. +func (c *Checker) markCircular(start int) { + c.currentNodeErrorsCyclic = true + c.extendCircularity(start + 1) +} + +// depthLimit counts the nesting of a recursion that is cut short at a fixed +// depth. How deep a computation may go then depends on how deep it was +// entered, so a computation cut short keeps its result only if it started +// counting. +type depthLimit struct { + depth int32 + start int32 // Index in resolutions of the computation that started counting +} + +// depthLimitReached reports whether l is at limit, in which case the +// computations since the one that started counting are marked circular. +func (c *Checker) depthLimitReached(l *depthLimit, limit int32) bool { + if l.depth < limit { + return false + } + c.markCircular(int(l.start)) + return true +} + +func (c *Checker) enterDepthLimit(l *depthLimit) { + if l.depth == 0 { + l.start = int32(len(c.resolutions) - 1) + } + l.depth++ +} + +func (l *depthLimit) exit() { + l.depth-- +} + /** * Pop an entry from the type resolution stack and return its associated result value. The result value will * be true if no circularities were detected, or false if a circularity was found. */ func (c *Checker) popTypeResolution() bool { - lastIndex := len(c.typeResolutions) - 1 - result := c.typeResolutions[lastIndex].result - c.typeResolutions[lastIndex] = TypeResolution{} // Clear the last entry to avoid memory leaks - c.typeResolutions = c.typeResolutions[:lastIndex] + n := len(c.resolutions) - 1 + debug.Assert(c.typeResolutionTop == n+1, "resolutions left above a type resolution") + f := &c.resolutions[n] + result := f.typeResolution.result + observed := c.observedHead + c.observedHead = minObservedHead(f.prevObservedHead, observed) + if r := &f.typeResolution; result && !r.participant && r.memo != nil && c.circularityStart != 0 && c.keepsResultInCycle(n, f, observed) { + r.participant = true + c.cycleParticipants = append(c.cycleParticipants, r.memo) + } + c.typeResolutionTop = int(f.prevTypeResolution) + c.dropResolution(n) + if len(c.cycleYields) != 0 && c.cycleYields[len(c.cycleYields)-1].end == n { + c.endCycleYield() + } return result } +// beginTypeResolution starts the resolution of propertyName for target, which +// re-entering detects as a cycle. Its resolution shares the work of the +// resolution below it. memo caches its result, if any. +func (c *Checker) beginTypeResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName, memo *memoHeader) { + f := c.pushResolution() + parent := &c.resolutions[len(c.resolutions)-2] + f.depthStart = parent.depthStart + f.serial = parent.serial + f.prevMemo = int32(c.memoTop) + f.isTypeResolution = true + f.prevTypeResolution = int32(c.typeResolutionTop) + f.prevObservedHead = c.observedHead + c.observedHead = 0 + f.typeResolution.target = target + f.typeResolution.memo = memo + f.typeResolution.propertyName = propertyName + f.typeResolution.result = true + c.typeResolutionTop = len(c.resolutions) +} + +// topTypeResolution returns the innermost resolution, which must be the +// innermost resolution. +func (c *Checker) topTypeResolution() *TypeResolution { + debug.Assert(c.typeResolutionTop == len(c.resolutions), "resolutions left above a type resolution") + return &c.resolutions[len(c.resolutions)-1].typeResolution +} + func (c *Checker) findResolutionCycleStartIndex(target TypeSystemEntity, propertyName TypeSystemPropertyName) int { - for i := len(c.typeResolutions) - 1; i >= c.resolutionStart; i-- { - resolution := &c.typeResolutions[i] + for i := c.typeResolutionTop - 1; i >= c.resolutionStart; i = int(c.resolutions[i].prevTypeResolution) - 1 { + resolution := &c.resolutions[i].typeResolution if c.typeResolutionHasProperty(resolution) { + // The result was set before its computation finished, which + // hides a cycle below it. Only then does the search depend on + // the result. + if c.memoActive() && c.findResolutionBelow(i, target, propertyName) { + if resolution.memo != nil { + c.observeCell(resolution.memo) + } + } return -1 } if resolution.target == target && resolution.propertyName == propertyName { @@ -19622,32 +20943,53 @@ func (c *Checker) findResolutionCycleStartIndex(target TypeSystemEntity, propert return -1 } +// findResolutionBelow reports whether a type resolution of target below the +// one at index i is in progress. +func (c *Checker) findResolutionBelow(i int, target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { + for i = int(c.resolutions[i].prevTypeResolution) - 1; i >= c.resolutionStart; i = int(c.resolutions[i].prevTypeResolution) - 1 { + r := &c.resolutions[i].typeResolution + if r.target == target && r.propertyName == propertyName { + return true + } + } + return false +} + +// typeResolutionHasProperty reports whether a resolution on the stack already +// has a result. Its computation is in progress, so the result is read without +// observing it; see findResolutionCycleStartIndex. func (c *Checker) typeResolutionHasProperty(r *TypeResolution) bool { switch r.propertyName { case TypeSystemPropertyNameType: - return c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).resolvedType != nil + return c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).resolvedType.peek() != nil case TypeSystemPropertyNameDeclaredType: - return c.typeAliasLinks.Get(r.target.(*ast.Symbol)).declaredType != nil + return c.typeAliasLinks.Get(r.target.(*ast.Symbol)).declaredType.peek() != nil case TypeSystemPropertyNameResolvedTypeArguments: - return r.target.(*Type).AsTypeReference().resolvedTypeArguments != nil + return r.target.(*Type).AsTypeReference().resolvedTypeArguments.peek() != nil case TypeSystemPropertyNameResolvedBaseTypes: return r.target.(*Type).AsInterfaceType().baseTypesResolved case TypeSystemPropertyNameResolvedBaseConstructorType: - return r.target.(*Type).AsInterfaceType().resolvedBaseConstructorType != nil + return r.target.(*Type).AsInterfaceType().resolvedBaseConstructorType.peek() != nil case TypeSystemPropertyNameResolvedReturnType: - return r.target.(*Signature).resolvedReturnType != nil + return r.target.(*Signature).resolvedReturnType.peek() != nil case TypeSystemPropertyNameResolvedBaseConstraint: - return r.target.(*Type).AsConstrainedType().resolvedBaseConstraint != nil + return r.target.(*Type).AsConstrainedType().resolvedBaseConstraint.peek() != nil case TypeSystemPropertyNameInitializerIsUndefined: return c.nodeLinks.Get(r.target.(*ast.Node)).flags&NodeCheckFlagsInitializerIsUndefinedComputed != 0 case TypeSystemPropertyNameWriteType: - return c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).getWriteType() != nil + extra := c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).extra + return extra != nil && extra.writeType.peek() != nil case TypeSystemPropertyNameAliasTarget: - return c.aliasSymbolLinks.Get(r.target.(*ast.Symbol)).aliasTarget != nil + return c.aliasSymbolLinks.Get(r.target.(*ast.Symbol)).aliasTarget.peek() != nil case TypeSystemPropertyNameComputedPropertyName: - return c.typeNodeLinks.Get(r.target.(*ast.Node)).resolvedType != nil + return c.typeNodeLinks.Get(r.target.(*ast.Node)).resolvedType.peek() != nil + case TypeSystemPropertyNameVariances: + return c.varianceLinks.Get(r.target.(*ast.Symbol)).variances.peek() != nil + default: + // Marked as checked or resolved on entry, so it is only on the stack + // while in progress. + return false } - panic("Unhandled case in typeResolutionHasProperty") } func (c *Checker) reportCircularityError(symbol *ast.Symbol) *Type { @@ -19684,7 +21026,7 @@ func (c *Checker) getPropertiesOfType(t *Type) []*ast.Symbol { func (c *Checker) getPropertiesOfObjectType(t *Type) []*ast.Symbol { if t.flags&TypeFlagsObject != 0 { - return c.resolveStructuredTypeMembers(t).getProperties() + return c.resolveStructuredTypeMembers(t).getProperties(c) } return nil } @@ -19715,28 +21057,31 @@ func (c *Checker) getLazyStateOfType(t *Type) *LazyState { func (c *Checker) getPropertiesOfUnionOrIntersectionType(t *Type) []*ast.Symbol { d := t.AsUnionOrIntersectionType() - if d.resolvedProperties == nil { - var checked collections.Set[string] - props := []*ast.Symbol{} - for _, current := range d.types { - for _, prop := range c.getPropertiesOfType(current) { - if !checked.Has(prop.Name()) { - checked.Add(prop.Name()) - combinedProp := c.getPropertyOfUnionOrIntersectionType(t, prop.Name(), t.flags&TypeFlagsIntersection != 0 /*skipObjectFunctionPropertyAugment*/) - if combinedProp != nil { - props = append(props, combinedProp) - } + if props := d.resolvedProperties.get(c); props != nil { + return props + } + d.resolvedProperties.begin(c) + var checked collections.Set[string] + props := []*ast.Symbol{} + for _, current := range d.types { + for _, prop := range c.getPropertiesOfType(current) { + if !checked.Has(prop.Name()) { + checked.Add(prop.Name()) + combinedProp := c.getPropertyOfUnionOrIntersectionType(t, prop.Name(), t.flags&TypeFlagsIntersection != 0 /*skipObjectFunctionPropertyAugment*/) + if combinedProp != nil { + props = append(props, combinedProp) } } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type without index signature - if t.flags&TypeFlagsUnion != 0 && len(c.getIndexInfosOfType(current)) == 0 { - break - } } - d.resolvedProperties = props + // The properties of a union type are those that are present in all constituent types, so + // we only need to check the properties of the first type without index signature + if t.flags&TypeFlagsUnion != 0 && len(c.getIndexInfosOfType(current)) == 0 { + break + } } - return d.resolvedProperties + d.resolvedProperties.set(c, props) + c.endMemo() + return props } func (c *Checker) getPropertyOfType(t *Type, name string) *ast.Symbol { @@ -19761,7 +21106,7 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr } } resolved := c.resolveStructuredTypeMembers(t) - symbol := resolved.getMember(name) + symbol := resolved.getMember(c, name) if symbol != nil { if !includeTypeOnlyMembers && t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsValueModule != 0 && c.moduleSymbolLinks.Get(t.symbol).typeOnlyExportStarMap[name] != nil { // If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over @@ -19811,9 +21156,15 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr // means that base classes of a class or interface can access members declared in the class or interface // during its resolution without causing a circularity error. Members resolved by this function are // eventually propagated into the resolved members of the class or interface. +// +// The answer depends on the resolution in progress, so it is observed as a +// cycle through it. func (c *Checker) getDeclaredPropertyOfResolvingType(t *Type, name string) *ast.Symbol { for i := len(c.resolvingObjectTypeStack) - 1; i >= 0; i-- { if c.resolvingObjectTypeStack[i].t == t { + if !c.isResolutionInProgress(t, TypeSystemPropertyNameStructuredMembers) { + return nil + } resolved := c.resolvingObjectTypeStack[i].resolvedMembers.Get(name) if resolved == nil { if symbol := c.resolveDeclaredMembers(getTargetType(t)).declaredMembers.Get(name); symbol != nil && c.isNamedMember(symbol, name) { @@ -19955,30 +21306,239 @@ func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool { } func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType { - if t.objectFlags&ObjectFlagsMembersResolved == 0 { - switch { - case t.flags&TypeFlagsObject != 0: - switch { - case t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0: - c.resolveObjectTypeMembers(t) - case t.objectFlags&ObjectFlagsReverseMapped != 0: - c.resolveReverseMappedTypeMembers(t) - case t.objectFlags&ObjectFlagsAnonymous != 0: - c.resolveAnonymousTypeMembers(t) - case t.objectFlags&ObjectFlagsMapped != 0: - c.resolveMappedTypeMembers(t) - default: - panic("Unhandled case in resolveStructuredTypeMembers") + if t.objectFlags&ObjectFlagsMembersResolved != 0 && !c.observingMemos { + return t.asResolvedStructuredType() + } + return c.resolveStructuredTypeMembersSlow(t) +} + +// resolveStructuredTypeMembersSlow is the part of resolveStructuredTypeMembers +// past reading resolved members, out of line so that the rest inlines. +func (c *Checker) resolveStructuredTypeMembersSlow(t *Type) *StructuredType { + key := memoKeyOf(t, memoStructuredMembers) + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + // Resolved members are only read. + c.observeMemo(key) + return t.AsStructuredType() + } + published := false + enclosing, inProgress := c.resolvingAnyIndex(key) + if !inProgress { + enclosing = -1 + } else { + // Resolving members may observe the type itself, whose members are + // then incomplete. + if c.isModuleTableType(t) { + if start := c.observeCircularHead(t, TypeSystemPropertyNameStructuredMembers, c.compareModuleTables); start >= 0 { + // When the table is canonical, the computations that read + // its partial members keep their results, as they would + // had the table been entered first. + c.addCycleParticipants(start) } - case t.flags&TypeFlagsUnion != 0: - c.resolveUnionTypeMembers(t) - case t.flags&TypeFlagsIntersection != 0: - c.resolveIntersectionTypeMembers(t) + } else { + c.isResolutionInProgress(t, TypeSystemPropertyNameStructuredMembers) + } + if j, ok := c.resolving.get(key); ok && int(j) < c.visibleStart { + // The members are being resolved below the cycle yield in + // progress, which then sees whatever part of them that + // resolution has published, rather than compute them from + // scratch, so the yield fails. + c.markResolutionCycle(c.visibleStart) + } + published = c.resolutions[enclosing].membersPublished + } + c.beginMemo(key) + if !published { + c.resolveStructuredTypeMembersUncached(t, enclosing) + } + c.endMemo() + return t.AsStructuredType() +} + +// resolveStructuredTypeMembersUncached resolves the members of t in the memo +// just begun for them. Many kinds of types make members available before +// they are complete, through publishStructuredTypeMembers, so the resolution +// is on the stack, but never fails. The members are resolved only once the +// outermost resolution of them completes. A nested one, which re-entering +// the members before any are available starts, makes its result available +// to the rest of the enclosing resolution at index enclosing, which may be +// hidden by a cycle yield. +func (c *Checker) resolveStructuredTypeMembersUncached(t *Type, enclosing int) { + outermost := enclosing < 0 + if outermost && c.isModuleTableType(t) { + c.resolveModuleTableMembers(t) + return + } + c.beginTypeResolution(t, TypeSystemPropertyNameStructuredMembers, nil) + defer func() { + c.popTypeResolution() + if outermost { + t.objectFlags |= ObjectFlagsMembersResolved + } else { + c.resolutions[enclosing].membersPublished = true + } + if c.logResults() { + c.logMembersDiscard(t) + } + }() + switch { + case t.flags&TypeFlagsObject != 0: + switch { + case t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0: + c.resolveObjectTypeMembers(t) + case t.objectFlags&ObjectFlagsReverseMapped != 0: + c.resolveReverseMappedTypeMembers(t) + case t.objectFlags&ObjectFlagsAnonymous != 0: + c.resolveAnonymousTypeMembers(t) + case t.objectFlags&ObjectFlagsMapped != 0: + c.resolveMappedTypeMembers(t) default: panic("Unhandled case in resolveStructuredTypeMembers") } + case t.flags&TypeFlagsUnion != 0: + c.resolveUnionTypeMembers(t) + case t.flags&TypeFlagsIntersection != 0: + c.resolveIntersectionTypeMembers(t) + default: + panic("Unhandled case in resolveStructuredTypeMembers") + } +} + +// markMembersPublished records that the members of t, whose resolution is +// in progress, are available to computations re-entering it. +func (c *Checker) markMembersPublished(t *Type) { + i, ok := c.resolvingAnyIndex(memoKeyOf(t, memoStructuredMembers)) + debug.Assert(ok, "members published outside their resolution") + c.resolutions[i].membersPublished = true +} + +// publishStructuredTypeMembers sets the members of t while they are being +// resolved, making them available to computations re-entering the resolution +// before it completes. +func (c *Checker) publishStructuredTypeMembers(t *Type, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { + // Listing the properties may re-enter the resolution, which then sees + // the members. + c.markMembersPublished(t) + c.setStructuredTypeData(t, members, callSignatures, constructSignatures, indexInfos) +} + +// isModuleTableType reports whether t is the type of a module object, whose +// members are the exports of the module. +func (c *Checker) isModuleTableType(t *Type) bool { + if t.objectFlags&ObjectFlagsAnonymous == 0 || t.symbol == nil || t.AsObjectType().target != nil { + return false + } + flags := c.getMergedSymbol(t.symbol).Flags() + return flags&ast.SymbolFlagsValueModule != 0 && flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsTypeLiteral) == 0 +} + +// compareModuleTables orders the resolutions of module object types in a +// cycle by the declarations of their modules, before any other resolutions. +func (c *Checker) compareModuleTables(a, b TypeSystemEntity) int { + ta, tb := a.(*Type), b.(*Type) + ma, mb := c.isModuleTableType(ta), c.isModuleTableType(tb) + switch { + case ma && mb: + return c.compareSymbols(ta.symbol, tb.symbol) + case ma: + return -1 + case mb: + return 1 + } + return 0 +} + +// resolveModuleTableMembers resolves the members of a module object type. +// Members are available while they are resolved, so module tables that +// read each other's members see the partial members of whichever was +// entered first. The table of the module first in program order is always +// resolved first, discarding whatever was computed starting from another, +// and a table that a cycle entered elsewhere passes through is resolved +// again as if the cycle had been entered at the table. +func (c *Checker) resolveModuleTableMembers(t *Type) { + mark := c.markDiscard() + hidden := false + for { + c.beginTypeResolution(t, TypeSystemPropertyNameStructuredMembers, nil) + c.resolveAnonymousTypeMembers(t) + restart := c.topTypeResolution().restartHidden && !hidden + if restart { + // Discard while the cycle is still in progress, as + // popCircularHead does. + c.discardSince(mark) + } + canonical := c.popCircularHead(mark) + if canonical == nil && !restart { + break + } + c.clearStructuredMembers(t) + key := memoKeyOf(t, memoStructuredMembers) + i, _ := c.resolving.get(key) + c.resolutions[i].membersPublished = false + if canonical != nil { + c.resolveStructuredTypeMembers(canonical.(*Type)) + continue + } + // A cycle entered below the table passed through it, so the table + // is resolved again as if it had been entered first: with the + // computations in progress below it hidden. + hidden = true + c.beginCycleYield(t, TypeSystemPropertyNameStructuredMembers, -1, int(i)) + } + if hidden { + c.endCycleYield() + } + t.objectFlags |= ObjectFlagsMembersResolved + c.releaseDiscardMark(mark) + if c.logResults() { + c.logMembersDiscard(t) } - return t.AsStructuredType() +} + +func (c *Checker) clearStructuredMembers(t *Type) { + t.objectFlags &^= ObjectFlagsMembersResolved + data := t.AsStructuredType() + data.members = nil + data.properties = nil + if data.extra != nil { + data.extra.signatures = nil + data.extra.callSignatureCount = 0 + data.extra.indexInfos = nil + } +} + +// logMembersDiscard discards the members of t when the cycle in progress +// ends, like any other result cached during the cycle, so that they are +// resolved again from scratch when next requested. +// +//go:noinline +func (c *Checker) logMembersDiscard(t *Type) { + c.logRestore(func() { + if _, ok := c.resolvingAnyIndex(memoKeyOf(t, memoStructuredMembers)); ok { + // An enclosing resolution of the same members sets them again. + return + } + c.clearStructuredMembers(t) + if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { + t.AsTypeReference().lazy = nil + } + }) +} + +// logPropertySlotDiscard discards the property in slot index of t, filled on +// first use during a cycle, when the cycle ends. The property may be one of a +// base type whose members the cycle discards. +// +//go:noinline +func (c *Checker) logPropertySlotDiscard(t *StructuredType, lazy *LazyState, index int) { + properties := t.properties + c.logRestore(func() { + properties[index] = nil + lazy.propertiesResolved = false + if t.AsType().AsTypeReference().lazy == lazy { + t.members = nil + } + }) } func (c *Checker) resolveObjectTypeMembers(t *Type) { @@ -20032,7 +21592,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) { return findIndexInfo(indexInfos, info.keyType) == nil })) } - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) + c.setStructuredTypeData(t, nil, callSignatures, constructSignatures, indexInfos) lazyState := c.lazyStateArena.New() lazyState.layout = layout lazyState.mapper = mapper @@ -20063,15 +21623,12 @@ func (c *Checker) getMemberLayout(t *Type, baseTypes []*Type) *MemberLayout { } source := c.getTargetType(t) key := c.getMemberLayoutKey(source, baseTypes) - layout := c.memberLayoutCache[key] - if layout == nil { - layout = c.createMemberLayout(source, baseTypes) - if c.memberLayoutCache == nil { - c.memberLayoutCache = make(map[CacheHashKey]*MemberLayout) - } - c.memberLayoutCache[key] = layout + if layout, ok := c.memberLayoutCache.get(c, key); ok { + return layout } - return layout + return c.memberLayoutCache.compute(c, key, func() *MemberLayout { + return c.createMemberLayout(source, baseTypes) + }) } func (c *Checker) getMemberLayoutKey(t *Type, baseTypes []*Type) CacheHashKey { @@ -20150,17 +21707,36 @@ func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { } func (c *Checker) getBaseTypes(t *Type) []*Type { + if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 { + return nil + } + if data := t.AsInterfaceType(); data.baseTypesResolved { + // Observing the result is what re-entering its computation amounts + // to, without the resolution. Walks over base types such as + // hasBaseType revisit shared bases many times. + if c.memoActive() { + c.observeMemo(memoKeyOf(t, memoBaseTypes)) + } + return data.resolvedBaseTypes.get(c) + } + c.beginMemo(memoKeyOf(t, memoBaseTypes)) + result := c.getBaseTypesUncached(t) + c.endMemo() + return result +} + +func (c *Checker) getBaseTypesUncached(t *Type) []*Type { if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 { return nil } data := t.AsInterfaceType() if !data.baseTypesResolved { - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseTypes) { - return data.resolvedBaseTypes + if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseTypes, &data.resolvedBaseTypes.memoHeader) { + return data.resolvedBaseTypes.get(c) } switch { case t.objectFlags&ObjectFlagsTuple != 0: - data.resolvedBaseTypes = []*Type{c.getTupleBaseType(t)} + data.resolvedBaseTypes.set(c, []*Type{c.getTupleBaseType(t)}) case t.symbol.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0: if t.symbol.Flags()&ast.SymbolFlagsClass != 0 { c.resolveBaseTypesOfClass(t) @@ -20184,8 +21760,11 @@ func (c *Checker) getBaseTypes(t *Type) []*Type { // See https://github.com/microsoft/TypeScript/issues/16861 for an example. t.objectFlags &^= ObjectFlagsMembersResolved data.baseTypesResolved = true + if c.logResults() { + c.discardLog = append(c.discardLog, discardEntry{memo: &data.resolvedBaseTypes.memoHeader, restore: func() { data.baseTypesResolved = false }}) + } } - return data.resolvedBaseTypes + return data.resolvedBaseTypes.get(c) } func (c *Checker) getTupleBaseType(t *Type) *Type { @@ -20246,7 +21825,7 @@ func (c *Checker) resolveBaseTypesOfClass(t *Type) { c.error(t.symbol.ValueDeclaration(), diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.TypeToString(t)) return } - t.AsInterfaceType().resolvedBaseTypes = []*Type{reducedBaseType} + t.AsInterfaceType().resolvedBaseTypes.set(c, []*Type{reducedBaseType}) } func getBaseTypeNodeOfClass(t *Type) *ast.Node { @@ -20285,7 +21864,7 @@ func (c *Checker) getSignatureInstantiation(sig *Signature, typeArguments []*Typ newReturnType := c.getOrCreateTypeFromSignature(newReturnSignature) newReturnType.AsObjectType().mapper = instantiatedSignature.mapper newInstantiatedSignature := c.cloneSignature(instantiatedSignature) - newInstantiatedSignature.resolvedReturnType = newReturnType + newInstantiatedSignature.resolvedReturnType.intern(newReturnType) return newInstantiatedSignature } } @@ -20302,10 +21881,12 @@ func (c *Checker) cloneSignature(sig *Signature) *Signature { func (c *Checker) getSignatureInstantiationWithoutFillingInTypeArguments(sig *Signature, typeArguments []*Type) *Signature { key := CachedSignatureKey{sig: sig, key: getTypeListKey(typeArguments)} - instantiation := c.cachedSignatures[key] + instantiation, _ := c.cachedSignatures.get(c, key) if instantiation == nil { + c.cachedSignatures.begin(c, key) instantiation = c.createSignatureInstantiation(sig, typeArguments) - c.cachedSignatures[key] = instantiation + c.cachedSignatures.set(c, key, instantiation) + c.endMemo() } return instantiation } @@ -20383,10 +21964,12 @@ func (c *Checker) getErasedSignature(signature *Signature) *Signature { return signature } key := CachedSignatureKey{sig: signature, key: SignatureKeyErased} - erased := c.cachedSignatures[key] + erased, _ := c.cachedSignatures.get(c, key) if erased == nil { + c.cachedSignatures.begin(c, key) erased = c.instantiateSignatureEx(signature, newArrayToSingleTypeMapper(signature.TypeParameters(), c.anyType), true /*eraseTypeParameters*/) - c.cachedSignatures[key] = erased + c.cachedSignatures.set(c, key, erased) + c.endMemo() } return erased } @@ -20396,10 +21979,12 @@ func (c *Checker) getCanonicalSignature(signature *Signature) *Signature { return signature } key := CachedSignatureKey{sig: signature, key: SignatureKeyCanonical} - canonical := c.cachedSignatures[key] + canonical, _ := c.cachedSignatures.get(c, key) if canonical == nil { + c.cachedSignatures.begin(c, key) canonical = c.createCanonicalSignature(signature) - c.cachedSignatures[key] = canonical + c.cachedSignatures.set(c, key, canonical) + c.endMemo() } return canonical } @@ -20426,9 +22011,10 @@ func (c *Checker) getBaseSignature(signature *Signature) *Signature { return signature } key := CachedSignatureKey{sig: signature, key: SignatureKeyBase} - if cached := c.cachedSignatures[key]; cached != nil { + if cached, _ := c.cachedSignatures.get(c, key); cached != nil { return cached } + c.cachedSignatures.begin(c, key) baseConstraintMapper := newTypeMapper(typeParameters, core.Map(typeParameters, func(tp *Type) *Type { return core.OrElse(c.getConstraintOfTypeParameter(tp), c.unknownType) })) @@ -20443,7 +22029,8 @@ func (c *Checker) getBaseSignature(signature *Signature) *Signature { // and then apply a type eraser to remove any remaining circularly dependent type parameters baseConstraints = c.instantiateTypes(baseConstraints, newArrayToSingleTypeMapper(typeParameters, c.anyType)) result := c.instantiateSignatureEx(signature, newTypeMapper(typeParameters, baseConstraints), true /*eraseTypeParameters*/) - c.cachedSignatures[key] = result + c.cachedSignatures.set(c, key, result) + c.endMemo() return result } @@ -20489,7 +22076,7 @@ func (c *Checker) resolveBaseTypesOfInterface(t *Type) { if !c.isErrorType(baseType) { if c.isValidBaseType(baseType) { if t != baseType && !c.hasBaseType(baseType, t) { - data.resolvedBaseTypes = append(data.resolvedBaseTypes, baseType) + data.resolvedBaseTypes.set(c, append(data.resolvedBaseTypes.get(c), baseType)) } else { c.reportCircularBaseType(declaration, t) } @@ -20603,16 +22190,28 @@ func (c *Checker) addInheritedMembers(symbols ast.SymbolTable, baseSymbols []*as } func (c *Checker) resolveDeclaredMembers(t *Type) *InterfaceType { + d := t.AsInterfaceType() + key := memoKeyOf(t, memoDeclaredMembers) + if d.declaredMembersResolved { + c.observeFlagMemo(key) + return d + } + c.beginMemo(key) + c.resolveDeclaredMembersUncached(t) + c.endMemo() + return d +} + +func (c *Checker) resolveDeclaredMembersUncached(t *Type) { d := t.AsInterfaceType() if !d.declaredMembersResolved { members := c.getMembersOfSymbol(t.symbol) - d.declaredMembersResolved = true + c.setFlagMemoValue(memoKeyOf(t, memoDeclaredMembers), &d.declaredMembersResolved, true) d.declaredMembers = members d.declaredCallSignatures = c.getSignaturesOfSymbol(d.declaredMembers.Get(ast.InternalSymbolNameCall)) d.declaredConstructSignatures = c.getSignaturesOfSymbol(d.declaredMembers.Get(ast.InternalSymbolNameNew)) d.declaredIndexInfos = c.getIndexInfosOfSymbol(t.symbol) } - return d } func (c *Checker) getIndexInfosOfSymbol(symbol *ast.Symbol) []*IndexInfo { @@ -20839,8 +22438,8 @@ func (c *Checker) getSignatureFromDeclaration(declaration *ast.Node) *Signature // of context-sensitive functions. func (c *Checker) getSignatureFromDeclarationUncontextual(declaration *ast.Node) *Signature { links := c.signatureLinks.Get(declaration) - if links.resolvedSignature != nil { - return links.resolvedSignature + if links.resolvedSignature.get(c) != nil { + return links.resolvedSignature.get(c) } var parameters []*ast.Symbol var flags SignatureFlags @@ -20909,14 +22508,14 @@ func (c *Checker) getSignatureFromDeclarationUncontextual(declaration *ast.Node) if ast.IsConstructorTypeNode(declaration) && ast.HasSyntacticModifier(declaration, ast.ModifierFlagsAbstract) || ast.IsConstructorDeclaration(declaration) && ast.HasSyntacticModifier(declaration.Parent, ast.ModifierFlagsAbstract) { flags |= SignatureFlagsAbstract } - if links.resolvedSignature != nil { + if existing := links.resolvedSignature.get(c); existing != nil { // Computing the signature reached the enclosing call, which // contextually typed the function with a signature of its own, as // when the call is checked first. Keep that one. - return links.resolvedSignature + return existing } - links.resolvedSignature = c.newSignature(flags, declaration, typeParameters, thisParameter, parameters, nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, minArgumentCount) - return links.resolvedSignature + links.resolvedSignature.set(c, c.newSignature(flags, declaration, typeParameters, thisParameter, parameters, nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, minArgumentCount)) + return links.resolvedSignature.get(c) } func (c *Checker) getTypeParametersFromDeclaration(declaration *ast.Node) []*Type { @@ -21009,10 +22608,20 @@ func isLateBindableAST(node *ast.Node) bool { } func (c *Checker) getReturnTypeOfSignature(sig *Signature) *Type { - if sig.resolvedReturnType != nil { - return sig.resolvedReturnType + if t := sig.resolvedReturnType.get(c); t != nil { + return t } - if !c.pushTypeResolution(sig, TypeSystemPropertyNameResolvedReturnType) { + sig.resolvedReturnType.begin(c) + result := c.getReturnTypeOfSignatureUncached(sig) + c.endMemo() + return result +} + +func (c *Checker) getReturnTypeOfSignatureUncached(sig *Signature) *Type { + if sig.resolvedReturnType.get(c) != nil { + return sig.resolvedReturnType.get(c) + } + if !c.pushTypeResolution(sig, TypeSystemPropertyNameResolvedReturnType, &sig.resolvedReturnType.memoHeader) { return c.errorType } var t *Type @@ -21037,25 +22646,31 @@ func (c *Checker) getReturnTypeOfSignature(sig *Signature) *Type { t = c.getOptionalType(t, false /*isProperty*/) } if !c.popTypeResolution() { - if sig.declaration != nil { - typeNode := sig.declaration.Type() - if typeNode != nil { - c.error(typeNode, diagnostics.Return_type_annotation_circularly_references_itself) - } else if c.noImplicitAny { - name := ast.GetNameOfDeclaration(sig.declaration) - if name != nil { - c.error(name, diagnostics.X_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, scanner.DeclarationNameToString(name)) - } else { - c.error(sig.declaration, diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions) - } + t = c.circularReturnType(sig) + } + if sig.resolvedReturnType.get(c) == nil { + sig.resolvedReturnType.set(c, t) + } + return sig.resolvedReturnType.get(c) +} + +// circularReturnType reports a return type that depends on itself and returns +// the type it resolves to instead. +func (c *Checker) circularReturnType(sig *Signature) *Type { + if sig.declaration != nil { + typeNode := sig.declaration.Type() + if typeNode != nil { + c.error(typeNode, diagnostics.Return_type_annotation_circularly_references_itself) + } else if c.noImplicitAny { + name := ast.GetNameOfDeclaration(sig.declaration) + if name != nil { + c.error(name, diagnostics.X_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, scanner.DeclarationNameToString(name)) + } else { + c.error(sig.declaration, diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions) } } - t = c.anyType } - if sig.resolvedReturnType == nil { - sig.resolvedReturnType = t - } - return sig.resolvedReturnType + return c.anyType } func (c *Checker) getNonCircularReturnTypeOfSignature(sig *Signature) *Type { @@ -21660,22 +23275,22 @@ func (c *Checker) instantiateIndexInfo(info *IndexInfo, m *TypeMapper) *IndexInf func (c *Checker) resolveAnonymousTypeMembers(t *Type) { d := t.AsObjectType() if d.target != nil { - c.setStructuredTypeMembers(t, nil, nil, nil, nil) + c.publishStructuredTypeMembers(t, nil, nil, nil, nil) members := c.createInstantiatedSymbolTable(c.getPropertiesOfObjectType(d.target), d.mapper) callSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindCall), d.mapper) constructSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindConstruct), d.mapper) indexInfos := c.instantiateIndexInfos(c.getIndexInfosOfType(d.target), d.mapper) - c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + c.publishStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) return } symbol := c.getMergedSymbol(t.symbol) if symbol.Flags()&ast.SymbolFlagsTypeLiteral != 0 { - c.setStructuredTypeMembers(t, nil, nil, nil, nil) + c.publishStructuredTypeMembers(t, nil, nil, nil, nil) members := c.getMembersOfSymbol(symbol) callSignatures := c.getSignaturesOfSymbol(members.Get(ast.InternalSymbolNameCall)) constructSignatures := c.getSignaturesOfSymbol(members.Get(ast.InternalSymbolNameNew)) indexInfos := c.getIndexInfosOfSymbol(symbol) - c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + c.publishStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) return } // Combinations of function, class, enum and module @@ -21700,7 +23315,7 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { baseConstructorIndexInfo = c.anyBaseTypeIndexInfo } } - c.setStructuredTypeMembers(t, members, nil, nil, nil) + c.publishStructuredTypeMembers(t, members, nil, nil, nil) var indexInfos []*IndexInfo if indexSymbol := members.Get(ast.InternalSymbolNameIndex); indexSymbol != nil { indexInfos = c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(members.Values())) @@ -21708,7 +23323,7 @@ func (c *Checker) resolveAnonymousTypeMembers(t *Type) { if baseConstructorIndexInfo != nil { indexInfos = append(indexInfos, baseConstructorIndexInfo) } - if symbol.Flags()&ast.SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || core.Some(d.getProperties(), func(prop *ast.Symbol) bool { + if symbol.Flags()&ast.SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || core.Some(d.getProperties(c), func(prop *ast.Symbol) bool { return c.getTypeOfSymbol(prop).flags&TypeFlagsNumberLike != 0 })) { indexInfos = append(indexInfos, c.enumNumberIndexInfo) @@ -21764,12 +23379,12 @@ func (c *Checker) instantiateSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symb } // If the type of the symbol is already resolved, and if that type could not possibly // be affected by instantiation, simply return the symbol itself. - if links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) { + if links.resolvedType.get(c) != nil && !c.couldContainTypeVariables(links.resolvedType.get(c)) { if symbol.Flags()&ast.SymbolFlagsSetAccessor == 0 { return symbol } // If we're a setter, check writeType. - if links.getWriteType() != nil && !c.couldContainTypeVariables(links.getWriteType()) { + if links.getWriteType(c) != nil && !c.couldContainTypeVariables(links.getWriteType(c)) { return symbol } } @@ -21879,7 +23494,7 @@ func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature { sig = c.cloneSignature(baseSig) } sig.setTypeParameters(c, classType.AsInterfaceType().LocalTypeParameters()) - sig.resolvedReturnType = classType + sig.resolvedReturnType.intern(classType) if isAbstract { sig.flags |= SignatureFlagsAbstract } else { @@ -21895,7 +23510,7 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { members := ast.NewSymbolTable() var indexInfos []*IndexInfo // Resolve upfront such that recursive references see an empty object type. - c.setStructuredTypeMembers(t, nil, nil, nil, nil) + c.publishStructuredTypeMembers(t, nil, nil, nil, nil) // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, // and T as the template type. typeParameter := c.getTypeParameterFromMappedType(t) @@ -21978,15 +23593,15 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { } else { forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType) } - c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) + c.publishStructuredTypeMembers(t, members, nil, nil, indexInfos) } func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { mappedType := links.getContainingType() - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { - mappedType.AsMappedType().containsError = true + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType, &links.resolvedType.memoHeader) { + mappedType.AsMappedType().containsError.setIdentity(c, true) return c.errorType } templateType := c.getTemplateTypeFromMappedType(core.OrElse(mappedType.AsMappedType().target, mappedType)) @@ -22002,17 +23617,17 @@ func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type { propType = c.removeMissingOrUndefinedType(propType) } if c.popTypeResolution() { - if links.resolvedType == nil { - links.resolvedType = propType + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, propType) } } else { - if links.resolvedType == nil { - links.resolvedType = c.errorType + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.errorType) } - c.error(c.currentNode, diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, c.symbolToString(symbol), c.TypeToString(mappedType)) + c.errorAtCurrentNode(diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, c.symbolToString(symbol), c.TypeToString(mappedType)) } } - return links.resolvedType + return links.resolvedType.get(c) } // Return the lower bound of the key type in a mapped type. Intuitively, the lower @@ -22065,7 +23680,7 @@ func (c *Checker) resolveUnionTypeMembers(t *Type) { return c.getSignaturesOfType(t, SignatureKindConstruct) })) indexInfos := c.getUnionIndexInfos(t.Types()) - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) + c.setStructuredTypeData(t, nil, callSignatures, constructSignatures, indexInfos) } func (c *Checker) getArrayMemberCallSignatures(t *Type) []*Signature { @@ -22267,18 +23882,18 @@ func (c *Checker) combineUnionOrIntersectionParameters(left *Signature, right *S core.IfElse(isRestParam, ast.CheckFlagsRestParameter, core.IfElse(isOptional, ast.CheckFlagsOptionalParameter, 0))) links := c.valueSymbolLinks.Get(paramSymbol) if isRestParam { - links.resolvedType = c.createArrayType(combinedParamType) + links.resolvedType.setIdentity(c, c.createArrayType(combinedParamType)) } else { - links.resolvedType = combinedParamType + links.resolvedType.setIdentity(c, combinedParamType) } params[i] = paramSymbol } if needsExtraRestElement { restParamSymbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, "args", ast.CheckFlagsRestParameter) links := c.valueSymbolLinks.Get(restParamSymbol) - links.resolvedType = c.createArrayType(c.getTypeAtPosition(shorter, longestCount)) + links.resolvedType.setIdentity(c, c.createArrayType(c.getTypeAtPosition(shorter, longestCount))) if shorter == right { - links.resolvedType = c.instantiateType(links.resolvedType, mapper) + links.resolvedType.setIdentity(c, c.instantiateType(links.resolvedType.get(c), mapper)) } params[longestCount] = restParamSymbol } @@ -22318,7 +23933,7 @@ func (c *Checker) resolveIntersectionTypeMembers(t *Type) { if len(signatures) != 0 && mixinCount > 0 { signatures = core.Map(signatures, func(s *Signature) *Signature { clone := c.cloneSignature(s) - clone.resolvedReturnType = c.includeMixinType(c.getReturnTypeOfSignature(s), types, mixinFlags, i) + clone.resolvedReturnType.intern(c.includeMixinType(c.getReturnTypeOfSignature(s), types, mixinFlags, i)) return clone }) } @@ -22329,7 +23944,7 @@ func (c *Checker) resolveIntersectionTypeMembers(t *Type) { indexInfos = c.appendIndexInfo(indexInfos, info, false /*union*/) } } - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) + c.setStructuredTypeData(t, nil, callSignatures, constructSignatures, indexInfos) } func (c *Checker) appendSignatures(signatures []*Signature, newSignatures []*Signature) []*Signature { @@ -22403,7 +24018,7 @@ func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, in func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { resolved := c.resolveStructuredTypeMembers(t) - symbol := resolved.getMember(name) + symbol := resolved.getMember(c, name) if symbol != nil && c.symbolIsValue(symbol) { return symbol } @@ -22426,29 +24041,28 @@ func (c *Checker) getPropertyOfUnionOrIntersectionType(t *Type, name string, ski // these partial properties when identifying discriminant properties, but otherwise they are filtered out // and do not appear to be present in the union type. func (c *Checker) getUnionOrIntersectionProperty(t *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol { - data := t.AsUnionOrIntersectionType() + d := t.AsUnionOrIntersectionType() + cache := &d.propertyCache if skipObjectFunctionPropertyAugment { - if prop := data.propertyCacheWithoutFunctionPropertyAugment.Get(name); prop != nil { - return prop - } - } else { - if prop := data.propertyCache.Get(name); prop != nil { - return prop - } + cache = &d.propertyCacheWithoutFunctionPropertyAugment + } + if prop, _ := cache.get(c, name); prop != nil { + return prop + } + if !skipObjectFunctionPropertyAugment { // Complete non-augmented properties are also valid for augmented // lookup. Share the entry instead of retaining it in both tables. - if prop := data.propertyCacheWithoutFunctionPropertyAugment.Get(name); prop != nil && prop.CheckFlags()&ast.CheckFlagsPartial == 0 { + if prop, _ := d.propertyCacheWithoutFunctionPropertyAugment.get(c, name); prop != nil && prop.CheckFlags()&ast.CheckFlagsPartial == 0 { return prop } } + // The property is created once, so its identity is kept. + cache.begin(c, name) prop := c.createUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment) if prop != nil { - if skipObjectFunctionPropertyAugment { - ast.GetSymbolTable(&data.propertyCacheWithoutFunctionPropertyAugment).Set(name, prop) - } else { - ast.GetSymbolTable(&data.propertyCache).Set(name, prop) - } + cache.setIdentity(c, name, prop) } + c.endMemo() return prop } @@ -22593,7 +24207,7 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s var singlePropMapper *TypeMapper if singleProp.Flags()&ast.SymbolFlagsTransient != 0 { links := c.valueSymbolLinks.Get(singleProp) - singlePropType = links.resolvedType + singlePropType = links.resolvedType.get(c) singlePropMapper = links.mapper } clone := c.createSymbolWithType(singleProp, singlePropType) @@ -22603,7 +24217,7 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s links := c.valueSymbolLinks.Get(clone) links.setContainingType(c, containingType) links.mapper = singlePropMapper - links.setWriteType(c, c.getWriteTypeOfSymbol(singleProp)) + links.setNewWriteType(c, c.getWriteTypeOfSymbol(singleProp)) return clone } if propSet.Size() == 0 { @@ -22669,15 +24283,15 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s return result } if isUnion { - links.resolvedType = c.getUnionType(propTypes) + links.resolvedType.setIdentity(c, c.getUnionType(propTypes)) } else { - links.resolvedType = c.getIntersectionType(propTypes) + links.resolvedType.setIdentity(c, c.getIntersectionType(propTypes)) } if writeTypes != nil { if isUnion { - links.setWriteType(c, c.getUnionType(writeTypes)) + links.setNewWriteType(c, c.getUnionType(writeTypes)) } else { - links.setWriteType(c, c.getIntersectionType(writeTypes)) + links.setNewWriteType(c, c.getIntersectionType(writeTypes)) } } return result @@ -22734,11 +24348,11 @@ type symbolWithTypeKey struct { // returning the same copy for the same property and type. func (c *Checker) getSymbolWithType(source *ast.Symbol, t *Type) *ast.Symbol { key := symbolWithTypeKey{source: source, t: t} - if existing := c.symbolsWithType[key]; existing != nil { + if existing, _ := c.symbolsWithType.get(key); existing != nil { return existing } result := c.createSymbolWithType(source, t) - c.symbolsWithType[key] = result + c.symbolsWithType.intern(key, result) return result } @@ -22746,11 +24360,11 @@ func (c *Checker) getSymbolWithType(source *ast.Symbol, t *Type) *ast.Symbol { // creates one with the given creation. func (c *Checker) internObjectType(symbol *ast.Symbol, flags ObjectFlags, members ast.SymbolTable, indexInfos []*IndexInfo, create func() *Type) *Type { key := c.getObjectTypeContentKey(symbol, flags&^(ObjectFlagsCouldContainTypeVariablesComputed|ObjectFlagsCouldContainTypeVariables|ObjectFlagsMembersResolved), members, indexInfos) - if existing := c.objectContentTypes[key]; existing != nil { + if existing, _ := c.objectContentTypes.get(key); existing != nil { return existing } result := create() - c.objectContentTypes[key] = result + c.objectContentTypes.intern(key, result) return result } @@ -22760,7 +24374,7 @@ func (c *Checker) createSymbolWithType(source *ast.Symbol, t *Type) *ast.Symbol symbol.SetParent(source.Parent()) symbol.SetValueDeclaration(source.ValueDeclaration()) links := c.valueSymbolLinks.Get(symbol) - links.resolvedType = t + links.resolvedType.setIdentity(c, t) links.target = source links.setNameType(c, c.valueSymbolLinks.Get(source).getNameType()) return symbol @@ -22817,10 +24431,7 @@ func (c *Checker) getApparentType(t *Type) *Type { func (c *Checker) getApparentTypeOfMappedType(t *Type) *Type { m := t.AsMappedType() - if m.resolvedApparentType == nil { - m.resolvedApparentType = c.getResolvedApparentTypeOfMappedType(t) - } - return m.resolvedApparentType + return c.memoCompute(&m.resolvedApparentType, func() *Type { return c.getResolvedApparentTypeOfMappedType(t) }) } func (c *Checker) getResolvedApparentTypeOfMappedType(t *Type) *Type { @@ -22850,16 +24461,17 @@ func (c *Checker) getResolvedApparentTypeOfMappedType(t *Type) *Type { func (c *Checker) getApparentTypeOfIntersectionType(t *Type, thisArgument *Type) *Type { if t == thisArgument { d := t.AsIntersectionType() - if d.resolvedApparentType == nil { - d.resolvedApparentType = c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/) - } - return d.resolvedApparentType + return c.memoCompute(&d.resolvedApparentType, func() *Type { + return c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/) + }) } key := CachedTypeKey{kind: CachedTypeKindApparentType, typeId: thisArgument.id} - result := c.cachedTypes[key] + result, _ := c.cachedTypes.get(c, key) if result == nil { + c.cachedTypes.begin(c, key) result = c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/) - c.cachedTypes[key] = result + c.cachedTypes.set(c, key, result) + c.endMemo() } return result } @@ -22874,27 +24486,29 @@ func (c *Checker) getReducedType(t *Type) *Type { switch { case t.flags&TypeFlagsUnion != 0: if t.objectFlags&ObjectFlagsContainsIntersections != 0 { - if reducedType := t.AsUnionType().resolvedReducedType; reducedType != nil { - return reducedType - } - reducedType := c.getReducedUnionType(t) - t.AsUnionType().resolvedReducedType = reducedType - return reducedType + return c.memoCompute(&t.AsUnionType().resolvedReducedType, func() *Type { return c.getReducedUnionType(t) }) } case t.flags&TypeFlagsIntersection != 0: - if t.objectFlags&ObjectFlagsIsNeverIntersectionComputed == 0 { - t.objectFlags |= ObjectFlagsIsNeverIntersectionComputed - if !c.isMappingOfSameObjectType(t.Types()) && c.somePropertyReducesToNever(t) { - t.objectFlags |= ObjectFlagsIsNeverIntersection - } - } - if t.objectFlags&ObjectFlagsIsNeverIntersection != 0 { + if c.isNeverIntersection(t) { return c.neverType } } return t } +// isNeverIntersection reports whether the intersection t reduces to never. +func (c *Checker) isNeverIntersection(t *Type) bool { + if c.objectFlagsKnown(t, ObjectFlagsIsNeverIntersectionComputed) { + return t.objectFlags&ObjectFlagsIsNeverIntersection != 0 + } + return c.objectFlagsCell(t, memoNeverIntersection, ObjectFlagsIsNeverIntersectionComputed, ObjectFlagsIsNeverIntersection, true /*inProgress*/, func() ObjectFlags { + if !c.isMappingOfSameObjectType(t.Types()) && c.somePropertyReducesToNever(t) { + return ObjectFlagsIsNeverIntersection + } + return 0 + }) != 0 +} + func (c *Checker) isMappingOfSameObjectType(types []*Type) bool { if len(types) != 0 && types[0].objectFlags&ObjectFlagsMapped != 0 { if firstType := c.getModifiersTypeFromMappedType(types[0]); firstType.flags&TypeFlagsObject != 0 { @@ -22943,7 +24557,7 @@ func (c *Checker) getReducedUnionType(unionType *Type) *Type { } reduced := c.getUnionType(reducedTypes) if reduced.flags&TypeFlagsUnion != 0 { - reduced.AsUnionType().resolvedReducedType = reduced + reduced.AsUnionType().resolvedReducedType.set(c, reduced) } return reduced } @@ -22961,7 +24575,7 @@ func (c *Checker) getReducedApparentType(t *Type) *Type { } func (c *Checker) elaborateNeverIntersection(chain *ast.Diagnostic, node *ast.Node, t *Type) *ast.Diagnostic { - if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsNeverIntersection != 0 { + if t.flags&TypeFlagsIntersection != 0 && c.isNeverIntersection(t) { neverProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), c.isDiscriminantWithNeverType) if neverProp != nil { return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(neverProp)) @@ -22987,42 +24601,56 @@ func isConflictingPrivateProperty(prop *ast.Symbol) bool { func (c *Checker) getTypeArguments(t *Type) []*Type { d := t.AsTypeReference() - if d.resolvedTypeArguments == nil { - n := d.target.AsInterfaceType() - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedTypeArguments) { - return slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters())) + if typeArguments := d.resolvedTypeArguments.get(c); typeArguments != nil { + return typeArguments + } + d.resolvedTypeArguments.begin(c) + result := c.getTypeArgumentsUncached(t) + c.endMemo() + return result +} + +func (c *Checker) getTypeArgumentsUncached(t *Type) []*Type { + d := t.AsTypeReference() + n := d.target.AsInterfaceType() + if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedTypeArguments, &d.resolvedTypeArguments.memoHeader) { + return slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters())) + } + var typeArguments []*Type + node := t.AsTypeReference().node + if node != nil { + switch node.Kind { + case ast.KindTypeReference: + typeArguments = append(n.OuterTypeParameters(), c.getEffectiveTypeArguments(node, n.LocalTypeParameters())...) + case ast.KindArrayType: + typeArguments = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)} + case ast.KindTupleType: + typeArguments = core.Map(node.Elements(), c.getTypeFromTypeNode) + default: + panic("Unhandled case in getTypeArguments") } - var typeArguments []*Type - node := t.AsTypeReference().node - if node != nil { - switch node.Kind { - case ast.KindTypeReference: - typeArguments = append(n.OuterTypeParameters(), c.getEffectiveTypeArguments(node, n.LocalTypeParameters())...) - case ast.KindArrayType: - typeArguments = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)} - case ast.KindTupleType: - typeArguments = core.Map(node.Elements(), c.getTypeFromTypeNode) - default: - panic("Unhandled case in getTypeArguments") - } + } + if c.popTypeResolution() { + if d.resolvedTypeArguments.peek() == nil { + d.resolvedTypeArguments.set(c, c.instantiateTypes(typeArguments, d.mapper)) } - if c.popTypeResolution() { - if d.resolvedTypeArguments == nil { - d.resolvedTypeArguments = c.instantiateTypes(typeArguments, d.mapper) - } + } else { + if d.resolvedTypeArguments.peek() == nil { + d.resolvedTypeArguments.set(c, slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters()))) + } + message := diagnostics.Tuple_type_arguments_circularly_reference_themselves + var args []any + if d.target.symbol != nil { + message = diagnostics.Type_arguments_for_0_circularly_reference_themselves + args = []any{c.symbolToString(d.target.symbol)} + } + if node != nil { + c.error(node, message, args...) } else { - if d.resolvedTypeArguments == nil { - d.resolvedTypeArguments = slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters())) - } - errorNode := core.IfElse(node != nil, node, c.currentNode) - if d.target.symbol != nil { - c.error(errorNode, diagnostics.Type_arguments_for_0_circularly_reference_themselves, c.symbolToString(d.target.symbol)) - } else { - c.error(errorNode, diagnostics.Tuple_type_arguments_circularly_reference_themselves) - } + c.errorAtCurrentNode(message, args...) } } - return d.resolvedTypeArguments + return d.resolvedTypeArguments.peek() } func (c *Checker) getEffectiveTypeArguments(node *ast.Node, typeParameters []*Type) []*Type { @@ -23089,6 +24717,17 @@ func (c *Checker) getDefaultTypeArgumentType(isInJavaScriptFile bool) *Type { // this gets the instantiated default type of its target. If the type parameter has no default type or // the default is circular, `undefined` is returned. func (c *Checker) getDefaultFromTypeParameter(t *Type) *Type { + key := memoKeyOf(t, memoTypeParameterDefault) + if t.flags&TypeFlagsTypeParameter != 0 && t.AsTypeParameter().resolvedDefaultType.peek() != nil && c.readsWithoutMemo(key) { + return c.getDefaultFromTypeParameterUncached(t) + } + c.beginMemo(key) + result := c.getDefaultFromTypeParameterUncached(t) + c.endMemo() + return result +} + +func (c *Checker) getDefaultFromTypeParameterUncached(t *Type) *Type { if t.flags&TypeFlagsTypeParameter == 0 { return nil } @@ -23101,39 +24740,69 @@ func (c *Checker) getDefaultFromTypeParameter(t *Type) *Type { func (c *Checker) getResolvedTypeParameterDefault(t *Type) *Type { d := t.AsTypeParameter() - if d.resolvedDefaultType == nil { + if d.resolvedDefaultType.get(c) == nil { if d.target != nil { targetDefault := c.getResolvedTypeParameterDefault(d.target) if targetDefault != nil { - d.resolvedDefaultType = c.instantiateType(targetDefault, d.mapper) + d.resolvedDefaultType.set(c, c.instantiateType(targetDefault, d.mapper)) } else { - d.resolvedDefaultType = c.noConstraintType + d.resolvedDefaultType.set(c, c.noConstraintType) } } else { - // To block recursion, set the initial value to the resolvingDefaultType. - d.resolvedDefaultType = c.resolvingDefaultType - defaultType := c.noConstraintType - if t.symbol != nil { - defaultDeclaration := core.FirstNonNil(t.symbol.Declarations(), func(decl *ast.Node) *ast.Node { - if ast.IsTypeParameterDeclaration(decl) { - return decl.AsTypeParameterDeclaration().DefaultType - } - return nil - }) - if defaultDeclaration != nil { - defaultType = c.getTypeFromTypeNode(defaultDeclaration) + c.resolveTypeParameterDefault(t) + } + } else if d.resolvedDefaultType.get(c) == c.resolvingDefaultType { + // If we are called recursively for this type parameter, mark the default as circular. + c.observeCircularHead(t, TypeSystemPropertyNameDefaultType, c.compareTypeParameterDeclarations) + d.resolvedDefaultType.swap(c.circularConstraintType) + return c.circularConstraintType + } + return d.resolvedDefaultType.get(c) +} + +// resolveTypeParameterDefault resolves the default of a declared type +// parameter. When defaults form a cycle, the first default resolved is the +// one found to be circular, so the default first in program order is always +// resolved first, discarding whatever was computed starting from another. +func (c *Checker) resolveTypeParameterDefault(t *Type) { + d := t.AsTypeParameter() + d.resolvedDefaultType.begin(c) + mark := c.markDiscard() + for { + // To block recursion, set the initial value to the resolvingDefaultType. + // Markers of a resolution in progress are never observed once it + // finishes, so they are not results to discard. + d.resolvedDefaultType.swap(c.resolvingDefaultType) + c.beginTypeResolution(t, TypeSystemPropertyNameDefaultType, nil) + defaultType := c.noConstraintType + if t.symbol != nil { + defaultDeclaration := core.FirstNonNil(t.symbol.Declarations(), func(decl *ast.Node) *ast.Node { + if ast.IsTypeParameterDeclaration(decl) { + return decl.AsTypeParameterDeclaration().DefaultType } - } - if d.resolvedDefaultType == c.resolvingDefaultType { - // If we have not been called recursively, set the correct default type. - d.resolvedDefaultType = defaultType + return nil + }) + if defaultDeclaration != nil { + defaultType = c.getTypeFromTypeNode(defaultDeclaration) } } - } else if d.resolvedDefaultType == c.resolvingDefaultType { - // If we are called recursively for this type parameter, mark the default as circular. - d.resolvedDefaultType = c.circularConstraintType + if d.resolvedDefaultType.swap(nil) != c.resolvingDefaultType { + // We were called recursively, so the default is circular. + defaultType = c.circularConstraintType + } + canonical := c.popCircularHead(mark) + if canonical == nil { + d.resolvedDefaultType.set(c, defaultType) + break + } + c.getResolvedTypeParameterDefault(canonical.(*Type)) } - return d.resolvedDefaultType + c.releaseDiscardMark(mark) + c.endMemo() +} + +func (c *Checker) compareTypeParameterDeclarations(a, b TypeSystemEntity) int { + return c.compareSymbols(a.(*Type).symbol, b.(*Type).symbol) } func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type { @@ -23179,21 +24848,33 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl if t == nil || m == nil || !(c.couldContainTypeVariables(t) || (t.alias != nil && len(t.alias.typeArguments) > 0 && core.Some(t.alias.typeArguments, c.couldContainTypeVariables))) { return t } - if len(c.instantiationStack) == 100 || c.instantiationCount >= 5_000_000 { - // We have reached 100 recursive type instantiations, or 5M type instantiations caused by the same statement - // or expression. There is a very high likelihood we're dealing with a combination of infinite generic types + // See "Instantiation limits" in memo.go. + depthLimitReached := c.instantiationDepthLimitReached() + if depthLimitReached || c.instantiationCount >= instantiationCountLimit { + // We have reached 100 recursive type instantiations in one computation, or 5M type instantiations caused by + // the same statement or expression. There is a very high likelihood we're dealing with a combination of infinite generic types // that perpetually generate new type identities, so we stop the recursion here by yielding the error type. if tr := c.tracer; tr != nil { tr.Instant(tracing.PhaseCheckTypes, "instantiateType_DepthLimit", map[string]any{"typeId": t.id, "instantiationDepth": len(c.instantiationStack), "instantiationCount": c.instantiationCount}) } circularTypeNames := c.getCircularTypeNames() + var message *diagnostics.Message + var args []any switch { case len(circularTypeNames) == 1: - c.error(c.currentNode, diagnostics.Instantiations_of_type_0_appear_infinitely_circular, circularTypeNames[0]) + message, args = diagnostics.Instantiations_of_type_0_appear_infinitely_circular, []any{circularTypeNames[0]} case len(circularTypeNames) > 1: - c.error(c.currentNode, diagnostics.Instantiations_of_the_following_types_appear_infinitely_circular_Colon_0, quotedAndCommaSeparated(circularTypeNames)) + message, args = diagnostics.Instantiations_of_the_following_types_appear_infinitely_circular_Colon_0, []any{quotedAndCommaSeparated(circularTypeNames)} default: - c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) + message = diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite + } + report := func(c *Checker, node *ast.Node) { c.error(node, message, args...) } + if depthLimitReached { + c.reportAtCurrentNode(report) + } else { + // The count depends on how much was already cached, so computing + // the result again elsewhere would not reliably reach it either. + c.reportAtCurrentNodeOnce(report) } return c.errorType } @@ -23204,8 +24885,11 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl c.pushActiveMapper(m) } else { key = getActiveMapperCacheKey(t, alias) - if cachedType, ok := c.activeTypeMappersCaches[index].get(key); ok { - return cachedType + // An instantiation cached in another resolution was computed against that + // resolution's depth limit, so it is only reused within the same + // resolution. + if cached, ok := c.activeTypeMappersCaches[index].get(key); ok && cached.serial == c.currentResolution().serial { + return cached.result } } c.TotalInstantiationCount++ @@ -23215,13 +24899,18 @@ func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAl if index == -1 { c.popActiveMapper() } else { - c.activeTypeMappersCaches[index].set(key, result) + c.activeTypeMappersCaches[index].set(key, activeMapperCacheEntry{result, c.currentResolution().serial}) } c.instantiationStack[len(c.instantiationStack)-1] = nil c.instantiationStack = c.instantiationStack[:len(c.instantiationStack)-1] return result } +type activeMapperCacheEntry struct { + result *Type + serial uint32 +} + // Without an alias the key is just the type ID, which avoids hashing. Hashed keys have // a nonzero high half with overwhelming probability, so the two kinds do not collide. func getActiveMapperCacheKey(t *Type, alias *TypeAlias) CacheHashKey { @@ -23236,21 +24925,45 @@ func getActiveMapperCacheKey(t *Type, alias *TypeAlias) CacheHashKey { func (c *Checker) getCircularTypeNames() []string { typeCounts := make(map[*Type]int) - var circularTypeNames []string - for _, t := range c.instantiationStack { + var circularSymbols []*ast.Symbol + // A computation reaching the depth limit names only its own + // instantiations, which do not depend on where it was first computed; + // see "Instantiation limits". + stack := c.instantiationStack + total := len(stack) == instantiationTotalDepthLimit + if !total { + stack = stack[c.currentResolution().depthStart:] + } + for _, t := range stack { typeCounts[t] = typeCounts[t] + 1 if typeCounts[t] == 3 { symbol := t.symbol if t.alias != nil { symbol = t.alias.symbol } - if symbol != nil && len(symbol.Name()) != 0 && symbol.Name()[0] != '\xFE' { - if name := c.SymbolToString(symbol); !slices.Contains(circularTypeNames, name) { - circularTypeNames = append(circularTypeNames, name) - } + if symbol != nil && len(symbol.Name()) != 0 && symbol.Name()[0] != '\xFE' && !slices.Contains(circularSymbols, symbol) { + circularSymbols = append(circularSymbols, symbol) } } } + if total && len(circularSymbols) > 1 { + // The whole instantiation is named, and which of the types in the + // cycle it entered first depends on what was cached before, so the + // cycle is named starting from the type declared first. + first := 0 + for i, s := range circularSymbols { + if c.compareSymbols(s, circularSymbols[first]) < 0 { + first = i + } + } + circularSymbols = append(circularSymbols[first:], circularSymbols[:first]...) + } + var circularTypeNames []string + for _, s := range circularSymbols { + if name := c.SymbolToString(s); !slices.Contains(circularTypeNames, name) { + circularTypeNames = append(circularTypeNames, name) + } + } return circularTypeNames } @@ -23262,7 +24975,7 @@ func (c *Checker) pushActiveMapper(mapper *TypeMapper) { // The cap may contain an empty cache from popActiveMapper; reuse it. c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex+1] } else { - c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, clearableMap[*Type]{}) + c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, clearableMap[activeMapperCacheEntry]{}) } } @@ -23293,16 +25006,21 @@ func (c *Checker) couldContainTypeVariablesWorker(t *Type) bool { if t.flags&TypeFlagsStructuredOrInstantiable == 0 { return false } - objectFlags := t.objectFlags - if objectFlags&ObjectFlagsCouldContainTypeVariablesComputed != 0 { - return objectFlags&ObjectFlagsCouldContainTypeVariables != 0 + if c.objectFlagsKnown(t, ObjectFlagsCouldContainTypeVariablesComputed) { + return t.objectFlags&ObjectFlagsCouldContainTypeVariables != 0 } + return c.objectFlagsCell(t, memoCouldContainTypeVariables, ObjectFlagsCouldContainTypeVariablesComputed, ObjectFlagsCouldContainTypeVariables, false /*inProgress*/, func() ObjectFlags { + return core.IfElse(c.computeCouldContainTypeVariables(t), ObjectFlagsCouldContainTypeVariables, 0) + }) != 0 +} + +func (c *Checker) computeCouldContainTypeVariables(t *Type) bool { + objectFlags := t.objectFlags result := t.flags&TypeFlagsInstantiable != 0 || t.flags&TypeFlagsObject != 0 && !c.isNonGenericTopLevelType(t) && (objectFlags&ObjectFlagsReference != 0 && (t.AsTypeReference().node != nil || core.Some(c.getTypeArguments(t), c.couldContainTypeVariables)) || objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags()&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) != 0 && t.symbol.Declarations() != nil || objectFlags&(ObjectFlagsMapped|ObjectFlagsReverseMapped|ObjectFlagsObjectRestType|ObjectFlagsInstantiationExpressionType) != 0) || t.flags&TypeFlagsUnionOrIntersection != 0 && t.flags&TypeFlagsEnumLiteral == 0 && !c.isNonGenericTopLevelType(t) && core.Some(t.Types(), c.couldContainTypeVariables) - t.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(result, ObjectFlagsCouldContainTypeVariables, 0) return result } @@ -23334,7 +25052,7 @@ func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias objectFlags := t.objectFlags if objectFlags&(ObjectFlagsReference|ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 { if objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil { - resolvedTypeArguments := t.AsTypeReference().resolvedTypeArguments + resolvedTypeArguments := t.AsTypeReference().resolvedTypeArguments.peek() newTypeArguments := c.instantiateTypes(resolvedTypeArguments, m) if core.Same(newTypeArguments, resolvedTypeArguments) { return t @@ -23424,14 +25142,15 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type links := c.typeNodeLinks.Get(declaration) switch { case t.objectFlags&ObjectFlagsReference != 0: // Deferred type reference - target = links.resolvedType + target = links.resolvedType.get(c) case t.objectFlags&ObjectFlagsInstantiated != 0: target = t.Target() default: target = t } - typeParameters = links.getOuterTypeParameters() + typeParameters = links.getOuterTypeParameters(c) if typeParameters == nil { + links.outerTypeParameters.begin(c) // The first time an anonymous type is instantiated we compute and store a list of the type // parameters that are in scope (and therefore potentially referenced). For type literals that // aren't the right hand side of a generic type alias declaration we optimize by reducing the @@ -23454,6 +25173,7 @@ func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *Type typeParameters = []*Type{} } links.setOuterTypeParameters(c, typeParameters) + c.endMemo() } if len(typeParameters) == 0 { return t @@ -23487,25 +25207,33 @@ 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[CacheHashKey]*Type) - data.instantiations[getTypeInstantiationKey(typeParameters, target.alias, false)] = target + if data.instantiations.isEmpty() { + data.instantiations.intern(getTypeInstantiationKey(typeParameters, target.alias, false), target) } - result := data.instantiations[key] + result, _ := data.instantiations.get(c, key) if result == nil { + reports := c.startCurrentNodeErrors() + data.instantiations.begin(c, key) result = instantiate(newTypeMapper(typeParameters, typeArguments)) - data.instantiations[key] = result + data.instantiations.setReported(c, key, result, c.endCurrentNodeErrors(reports)) + c.endMemo() if result.flags&TypeFlagsObjectFlagsType != 0 && result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 { // if `result` is one of the object types we tried to make (it may not be, due to how `instantiateMappedType` works), we can carry forward the type variable containment check from the input type arguments resultCouldContainObjectFlags := core.Some(typeArguments, c.couldContainTypeVariables) if result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 { + // The flags are part of creating the type, so they are kept for + // as long as the type is, even if created in a cycle that is + // discarded; computed again from the structure of the type, they + // may differ. if result.objectFlags&(ObjectFlagsMapped|ObjectFlagsAnonymous|ObjectFlagsReference) != 0 { result.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariables, 0) - } else { + c.setFlagMemo(memoKeyOf(result, memoCouldContainTypeVariables)) + } else if !resultCouldContainObjectFlags { // If none of the type arguments for the outer type parameters contain type variables, it follows // that the instantiated type doesn't reference type variables. // Intrinsics have `CouldContainTypeVariablesComputed` pre-set, so this should only cover unions and intersections resulting from `instantiateMappedType` - result.objectFlags |= core.IfElse(!resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariablesComputed, 0) + result.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed + c.setFlagMemo(memoKeyOf(result, memoCouldContainTypeVariables)) } } } @@ -23576,7 +25304,7 @@ func (c *Checker) instantiateAnonymousType(t *Type, m *TypeMapper, alias *TypeAl // C.f. instantiateSignature origTypeParameter := c.getTypeParameterFromMappedType(t) freshTypeParameter := c.cloneTypeParameter(origTypeParameter) - result.AsMappedType().typeParameter = freshTypeParameter + result.AsMappedType().typeParameter.intern(freshTypeParameter) m = c.combineTypeMappers(newSimpleTypeMapper(origTypeParameter, freshTypeParameter), m) freshTypeParameter.AsTypeParameter().mapper = m case t.objectFlags&ObjectFlagsInstantiationExpressionType != 0: @@ -23603,8 +25331,10 @@ func (c *Checker) getConditionalTypeInstantiation(t *Type, mapper *TypeMapper, f // instantiation cache key from the type IDs of the type arguments. typeArguments := core.Map(root.outerTypeParameters, mapper.Map) key := getConditionalTypeKey(typeArguments, alias, forConstraint) - result := root.instantiations[key] + result, _ := root.instantiations.get(c, key) if result == nil { + reports := c.startCurrentNodeErrors() + root.instantiations.begin(c, key) newMapper := newTypeMapper(root.outerTypeParameters, typeArguments) checkType := root.checkType var distributionType *Type @@ -23621,7 +25351,8 @@ func (c *Checker) getConditionalTypeInstantiation(t *Type, mapper *TypeMapper, f } else { result = c.getConditionalType(root, newMapper, forConstraint, alias) } - root.instantiations[key] = result + root.instantiations.setReported(c, key, result, c.endCurrentNodeErrors(reports)) + c.endMemo() } return result } @@ -23665,7 +25396,7 @@ func (c *Checker) instantiateMappedType(t *Type, m *TypeMapper, alias *TypeAlias return s } if d.declaration.NameType == nil { - if c.isArrayType(s) || s.flags&TypeFlagsAny != 0 && c.findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyNameResolvedBaseConstraint) < 0 && c.hasArrayOrTypeTypeConstraint(typeVariable) { + if c.isArrayType(s) || s.flags&TypeFlagsAny != 0 && !c.isResolutionInProgress(typeVariable, TypeSystemPropertyNameResolvedBaseConstraint) && c.hasArrayOrTypeTypeConstraint(typeVariable) { return c.instantiateMappedArrayType(s, t, prependTypeMapping(typeVariable, s, m)) } if isTupleType(s) { @@ -23782,18 +25513,16 @@ func getModifiedReadonlyState(state bool, modifiers MappedTypeModifiers) bool { func (c *Checker) getTypeParameterFromMappedType(t *Type) *Type { m := t.AsMappedType() - if m.typeParameter == nil { - m.typeParameter = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(m.declaration.TypeParameter)) - } - return m.typeParameter + return c.memoCompute(&m.typeParameter, func() *Type { + return c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(m.declaration.TypeParameter)) + }) } func (c *Checker) getConstraintTypeFromMappedType(t *Type) *Type { m := t.AsMappedType() - if m.constraintType == nil { - m.constraintType = core.OrElse(c.getConstraintOfTypeParameter(c.getTypeParameterFromMappedType(t)), c.errorType) - } - return m.constraintType + return c.memoCompute(&m.constraintType, func() *Type { + return core.OrElse(c.getConstraintOfTypeParameter(c.getTypeParameterFromMappedType(t)), c.errorType) + }) } func (c *Checker) getNameTypeFromMappedType(t *Type) *Type { @@ -23801,22 +25530,19 @@ func (c *Checker) getNameTypeFromMappedType(t *Type) *Type { if m.declaration.NameType == nil { return nil } - if m.nameType == nil { - m.nameType = c.instantiateType(c.getTypeFromTypeNode(m.declaration.NameType), m.mapper) - } - return m.nameType + return c.memoCompute(&m.nameType, func() *Type { + return c.instantiateType(c.getTypeFromTypeNode(m.declaration.NameType), m.mapper) + }) } func (c *Checker) getTemplateTypeFromMappedType(t *Type) *Type { m := t.AsMappedType() - if m.templateType == nil { + return c.memoCompute(&m.templateType, func() *Type { if m.declaration.Type != nil { - m.templateType = c.instantiateType(c.addOptionalityEx(c.getTypeFromTypeNode(m.declaration.Type) /*isProperty*/, true, getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0), m.mapper) - } else { - m.templateType = c.errorType + return c.instantiateType(c.addOptionalityEx(c.getTypeFromTypeNode(m.declaration.Type) /*isProperty*/, true, getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0), m.mapper) } - } - return m.templateType + return c.errorType + }) } func (c *Checker) isMappedTypeWithKeyofConstraintDeclaration(t *Type) bool { @@ -23918,26 +25644,67 @@ func (c *Checker) tryGetTypeFromTypeNode(node *ast.Node) *Type { } func (c *Checker) getTypeFromTypeNode(node *ast.Node) *Type { - return c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node) + if t := c.getKeywordTypeOfTypeNode(node); t != nil && !isConditionalFlowTypeLocation(node) { + // A keyword type is the same everywhere getConditionalFlowTypeOfType + // leaves it unchanged, and computing it has no effects. + return t + } + key := memoKeyOf(node, memoTypeNodeType) + if t := c.peekTypeFromTypeNode(node); t != nil && c.readsWithoutMemo(key) { + return t + } + c.beginMemo(key) + result := c.getTypeFromTypeNodeUncached(node) + c.endMemo() + return result } -func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type { +// memoTypeNode calls a getTypeFromXXXNode worker that caches its result in the +// links of the type node. +func memoTypeNode(c *Checker, node *ast.Node, worker func(*Checker, *ast.Node) *Type) *Type { + c.typeNodeLinks.Get(node).resolvedType.begin(c) + result := worker(c, node) + c.endMemo() + return result +} + +// peekTypeFromTypeNode returns the type of node if reading it only reads a +// cached result, or nil: for a kind whose type is cached in the node's links, +// at a location where getConditionalFlowTypeOfType leaves it unchanged. +func (c *Checker) peekTypeFromTypeNode(node *ast.Node) *Type { + switch node.Kind { + case ast.KindTypeReference, ast.KindExpressionWithTypeArguments, ast.KindUnionType, ast.KindIntersectionType, + ast.KindConditionalType, ast.KindIndexedAccessType, ast.KindArrayType, ast.KindTupleType, ast.KindThisType, + ast.KindTypeOperator, ast.KindLiteralType: + links := c.typeNodeLinks.TryGet(node) + if links == nil || links.resolvedType.peek() == nil || isConditionalFlowTypeLocation(node) { + return nil + } + return links.resolvedType.peek() + } + return nil +} + +// isConditionalFlowTypeLocation reports whether getConditionalFlowTypeOfType +// may change the type of node: whether it is within the true branch of a +// conditional type, or the template of a mapped type without an as clause. +func isConditionalFlowTypeLocation(node *ast.Node) bool { + for n := node; n != nil && !ast.IsStatement(n) && n.Kind != ast.KindJSDoc; n = n.Parent { + parent := n.Parent + if ast.IsConditionalTypeNode(parent) && n == parent.AsConditionalTypeNode().TrueType || + ast.IsMappedTypeNode(parent) && parent.AsMappedTypeNode().NameType == nil && n == parent.Type() { + return true + } + } + return false +} + +// getKeywordTypeOfTypeNode returns the type of a keyword type node, or nil if +// node is not one. +func (c *Checker) getKeywordTypeOfTypeNode(node *ast.Node) *Type { switch node.Kind { case ast.KindAnyKeyword, ast.KindJSDocAllType: return c.anyType - case ast.KindJSDocNonNullableType: - return c.getTypeFromTypeNode(node.Type()) - case ast.KindJSDocNullableType: - t := c.getTypeFromTypeNode(node.Type()) - if c.strictNullChecks { - return c.getNullableType(t, TypeFlagsNull) - } else { - return t - } - case ast.KindJSDocVariadicType: - return c.createArrayType(c.getTypeFromTypeNode(node.AsJSDocVariadicType().Type)) - case ast.KindJSDocOptionalType: - return c.addOptionality(c.getTypeFromTypeNode(node.Type())) case ast.KindUnknownKeyword: return c.unknownType case ast.KindStringKeyword: @@ -23962,6 +25729,32 @@ func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type { return c.nonPrimitiveType case ast.KindIntrinsicKeyword: return c.intrinsicMarkerType + } + return nil +} + +func (c *Checker) getTypeFromTypeNodeUncached(node *ast.Node) *Type { + return c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node) +} + +func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type { + if t := c.getKeywordTypeOfTypeNode(node); t != nil { + return t + } + switch node.Kind { + case ast.KindJSDocNonNullableType: + return c.getTypeFromTypeNode(node.Type()) + case ast.KindJSDocNullableType: + t := c.getTypeFromTypeNode(node.Type()) + if c.strictNullChecks { + return c.getNullableType(t, TypeFlagsNull) + } else { + return t + } + case ast.KindJSDocVariadicType: + return c.createArrayType(c.getTypeFromTypeNode(node.AsJSDocVariadicType().Type)) + case ast.KindJSDocOptionalType: + return c.addOptionality(c.getTypeFromTypeNode(node.Type())) case ast.KindThisType, ast.KindThisKeyword: return c.getTypeFromThisTypeNode(node) case ast.KindLiteralType: @@ -24012,10 +25805,10 @@ func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type { func (c *Checker) getTypeFromThisTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - links.resolvedType = c.getThisType(node) + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.getThisType(node)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getThisType(node *ast.Node) *Type { @@ -24037,66 +25830,66 @@ func (c *Checker) getTypeFromLiteralTypeNode(node *ast.Node) *Type { return c.nullType } links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - links.resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(node.AsLiteralTypeNode().Literal)) + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.getRegularTypeOfLiteralType(c.checkExpression(node.AsLiteralTypeNode().Literal))) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { // Deferred resolution of members is handled by resolveObjectTypeMembers alias := c.getAliasForTypeNode(node) if sym := node.Symbol(); sym == nil || c.getMembersOfSymbol(sym).Len() == 0 && alias == nil { - links.resolvedType = c.emptyTypeLiteralType + links.resolvedType.set(c, c.emptyTypeLiteralType) } else { t := c.newObjectType(ObjectFlagsAnonymous, node.Symbol()) t.alias = alias - links.resolvedType = t + links.resolvedType.set(c, t) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromIndexedAccessTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { objectType := c.getTypeFromTypeNode(node.AsIndexedAccessTypeNode().ObjectType) indexType := c.getTypeFromTypeNode(node.AsIndexedAccessTypeNode().IndexType) potentialAlias := c.getAliasForTypeNode(node) - links.resolvedType = c.getIndexedAccessTypeEx(objectType, indexType, AccessFlagsNone, node, potentialAlias) + links.resolvedType.set(c, c.getIndexedAccessTypeEx(objectType, indexType, AccessFlagsNone, node, potentialAlias)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromTypeOperatorNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { argType := node.Type() switch node.AsTypeOperatorNode().Operator { case ast.KindKeyOfKeyword: - links.resolvedType = c.getIndexType(c.getTypeFromTypeNode(argType)) + links.resolvedType.set(c, c.getIndexType(c.getTypeFromTypeNode(argType))) case ast.KindUniqueKeyword: if argType.Kind == ast.KindSymbolKeyword { - links.resolvedType = c.getESSymbolLikeTypeForNode(ast.WalkUpParenthesizedTypes(node.Parent)) + links.resolvedType.set(c, c.getESSymbolLikeTypeForNode(ast.WalkUpParenthesizedTypes(node.Parent))) } else { - links.resolvedType = c.errorType + links.resolvedType.set(c, c.errorType) } case ast.KindReadonlyKeyword: - links.resolvedType = c.getTypeFromTypeNode(argType) + links.resolvedType.set(c, c.getTypeFromTypeNode(argType)) default: panic("Unhandled case in getTypeFromTypeOperatorNode") } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getESSymbolLikeTypeForNode(node *ast.Node) *Type { if isValidESSymbolDeclaration(node) { symbol := c.getSymbolOfNode(node) if symbol != nil { - uniqueType := c.uniqueESSymbolTypes[symbol] + uniqueType, _ := c.uniqueESSymbolTypes.get(symbol) if uniqueType == nil { var b strings.Builder b.WriteString(ast.InternalSymbolNamePrefix) @@ -24105,7 +25898,7 @@ func (c *Checker) getESSymbolLikeTypeForNode(node *ast.Node) *Type { b.WriteByte('@') b.WriteString(strconv.FormatUint(uint64(ast.GetSymbolId(symbol)), 10)) uniqueType = c.newUniqueESSymbolType(symbol, b.String()) - c.uniqueESSymbolTypes[symbol] = uniqueType + c.uniqueESSymbolTypes.intern(symbol, uniqueType) } return uniqueType } @@ -24115,19 +25908,25 @@ func (c *Checker) getESSymbolLikeTypeForNode(node *ast.Node) *Type { func (c *Checker) getTypeFromTypeReference(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { // Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the // type reference in checkTypeReferenceNode. // handle LS queries on the `const` in `x as const` by resolving to the type of `x` if isConstTypeReference(node) && ast.IsAssertionExpression(node.Parent) { - links.resolvedType = c.checkExpressionCached(node.Parent.Expression()) + links.resolvedType.set(c, c.checkExpressionCached(node.Parent.Expression())) } else if t := c.getIntendedTypeFromJSDocTypeReference(node); t != nil { - links.resolvedType = t + links.resolvedType.set(c, t) } else { - links.resolvedType = c.getDistributedTypeParameter(node, c.getTypeReferenceType(node, c.getSymbolFromTypeReference(node))) + t := c.getDistributedTypeParameter(node, c.getTypeReferenceType(node, c.getSymbolFromTypeReference(node))) + if t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == node { + // A deferred reference is identified by the node, so it outlives a cycle. + links.resolvedType.setIdentity(c, t) + } else { + links.resolvedType.set(c, t) + } } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getDistributedTypeParameter(node *ast.Node, t *Type) *Type { @@ -24149,15 +25948,17 @@ func (c *Checker) getDistributedTypeFromTypeParameter(t *Type) *Type { tp := t.AsTypeParameter() if tp.distributedType == nil { tp.distributedType = c.newTypeParameter(t.symbol) - tp.distributedType.AsTypeParameter().isDistributed = true - tp.distributedType.AsTypeParameter().constraint = t + distributed := tp.distributedType.AsTypeParameter() + distributed.isDistributed = true + distributed.constraint.intern(t) + distributed.distributedType = t } return tp.distributedType } func getNonDistributedTypeParameter(t *Type) *Type { if t.flags&TypeFlagsTypeParameter != 0 && t.AsTypeParameter().isDistributed { - return t.AsTypeParameter().constraint + return t.AsTypeParameter().distributedType } return t } @@ -24224,13 +26025,20 @@ func (c *Checker) getIntendedTypeFromJSDocTypeReference(node *ast.Node) *Type { } func (c *Checker) getSymbolFromTypeReference(node *ast.Node) *ast.Symbol { + c.symbolNodeLinks.Get(node).resolvedSymbol.begin(c) + result := c.getSymbolFromTypeReferenceCached(node) + c.endMemo() + return result +} + +func (c *Checker) getSymbolFromTypeReferenceCached(node *ast.Node) *ast.Symbol { links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol == nil { + if links.resolvedSymbol.get(c) == nil { // The `const` in a `const` assertion resolves to nothing; resolveName knows not to // report an error for it, so no special-casing is needed here. - links.resolvedSymbol = c.resolveTypeReferenceName(node, ast.SymbolFlagsType, false /*ignoreErrors*/) + links.resolvedSymbol.set(c, c.resolveTypeReferenceName(node, ast.SymbolFlagsType, false /*ignoreErrors*/)) } - return links.resolvedSymbol + return links.resolvedSymbol.get(c) } func (c *Checker) resolveTypeReferenceName(typeReference *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool) *ast.Symbol { @@ -24273,12 +26081,12 @@ func (c *Checker) getUnresolvedSymbolForEntityName(name *ast.Node) *ast.Symbol { } else { path = text } - result := c.unresolvedSymbols[path] + result, _ := c.unresolvedSymbols.get(path) if result == nil { result = c.newSymbolEx(ast.SymbolFlagsTypeAlias, text, ast.CheckFlagsUnresolved) - c.unresolvedSymbols[path] = result + c.unresolvedSymbols.intern(path, result) result.SetParent(parentSymbol) - c.typeAliasLinks.Get(result).declaredType = c.unresolvedType + c.typeAliasLinks.Get(result).declaredType.intern(c.unresolvedType) } return result } @@ -24526,10 +26334,11 @@ func (n *TupleNormalizer) normalize(c *Checker, elementTypes []*Type, elementInf } else if isTupleType(t) { spreadTypes := c.getElementTypes(t) if len(spreadTypes)+len(n.types) >= 10_000 { - message := core.IfElse(c.currentNode != nil && ast.IsPartOfTypeNode(c.currentNode), - diagnostics.Type_produces_a_tuple_type_that_is_too_large_to_represent, - diagnostics.Expression_produces_a_tuple_type_that_is_too_large_to_represent) - c.error(c.currentNode, message) + c.reportAtCurrentNode(func(c *Checker, node *ast.Node) { + c.error(node, core.IfElse(node != nil && ast.IsPartOfTypeNode(node), + diagnostics.Type_produces_a_tuple_type_that_is_too_large_to_represent, + diagnostics.Expression_produces_a_tuple_type_that_is_too_large_to_represent)) + }) return false } // Spread variadic elements with tuple types into the resulting tuple. @@ -24731,16 +26540,16 @@ func (c *Checker) getTypeFromTypeAliasReference(node *ast.Node, symbol *ast.Symb if symbol.CheckFlags()&ast.CheckFlagsUnresolved != 0 { alias := &TypeAlias{symbol: symbol, typeArguments: core.Map(typeArguments, c.getTypeFromTypeNode)} key := getAliasKey(alias) - errorType := c.errorTypes[key] + errorType, _ := c.errorTypes.get(key) if errorType == nil { errorType = c.newIntrinsicType(TypeFlagsAny, "error") errorType.alias = alias - c.errorTypes[key] = errorType + c.errorTypes.intern(key, errorType) } return errorType } t := c.getDeclaredTypeOfSymbol(symbol) - typeParameters := c.typeAliasLinks.Get(symbol).typeParameters + typeParameters := c.getTypeParametersOfTypeAlias(symbol) if len(typeParameters) != 0 { numTypeArguments := len(typeArguments) minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters) @@ -24802,13 +26611,13 @@ func (c *Checker) getTypeAliasInstantiation(symbol *ast.Symbol, typeArguments [] links := c.typeAliasLinks.Get(symbol) typeParameters := links.typeParameters key := getTypeAliasInstantiationKey(typeArguments, alias) - instantiation := links.instantiations[key] - if instantiation == nil { - mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(symbol.ValueDeclaration()))) - instantiation = c.instantiateTypeWithAlias(t, mapper, alias) - links.instantiations[key] = instantiation + if instantiation, _ := links.instantiations.get(c, key); instantiation != nil { + return instantiation } - return instantiation + return links.instantiations.compute(c, key, func() *Type { + mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(symbol.ValueDeclaration()))) + return c.instantiateTypeWithAlias(t, mapper, alias) + }) } func isLocalTypeAlias(symbol *ast.Symbol) bool { @@ -24817,6 +26626,17 @@ func isLocalTypeAlias(symbol *ast.Symbol) bool { } func (c *Checker) getDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { + key := memoKeyOf(symbol, memoDeclaredType) + if t := c.peekDeclaredTypeOfSymbol(symbol); t != nil && c.readsWithoutMemo(key) { + return t + } + c.beginMemo(key) + result := c.getDeclaredTypeOfSymbolUncached(symbol) + c.endMemo() + return result +} + +func (c *Checker) getDeclaredTypeOfSymbolUncached(symbol *ast.Symbol) *Type { result := c.tryGetDeclaredTypeOfSymbol(symbol) if result == nil { result = c.errorType @@ -24824,6 +26644,22 @@ func (c *Checker) getDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { return result } +// peekDeclaredTypeOfSymbol returns the declared type of symbol if it is +// resolved, or nil. +func (c *Checker) peekDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { + switch { + case symbol.Flags()&ast.SymbolFlagsTypeAlias != 0 && symbol.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsTypeParameter) == 0: + if links := c.typeAliasLinks.TryGet(symbol); links != nil && c.memoErrorStates == 0 { + return links.declaredType.peek() + } + case symbol.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsTypeParameter|ast.SymbolFlagsEnum|ast.SymbolFlagsEnumMember|ast.SymbolFlagsAlias) != 0: + if links := c.declaredTypeLinks.TryGet(symbol); links != nil { + return links.declaredType.peek() + } + } + return nil +} + func (c *Checker) tryGetDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { switch { case symbol.Flags()&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0: @@ -24986,20 +26822,21 @@ func (c *Checker) appendTypeParameters(typeParameters []*Type, declarations []*a func (c *Checker) getDeclaredTypeOfTypeParameter(symbol *ast.Symbol) *Type { links := c.declaredTypeLinks.Get(symbol) - if links.declaredType == nil { - links.declaredType = c.newTypeParameter(symbol) + if links.declaredType.get(c) == nil { + links.declaredType.setIdentity(c, c.newTypeParameter(symbol)) } - return links.declaredType + return links.declaredType.get(c) } func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type { links := c.typeAliasLinks.Get(symbol) - if links.declaredType == nil { + if links.declaredType.get(c) == nil { // Note that we use the links object as the target here because the symbol object is used as the unique // identity for resolution of the 'type' property in SymbolLinks. - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameDeclaredType) { + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameDeclaredType, &links.declaredType.memoHeader) { return c.errorType } + reports := c.startCurrentNodeErrors() declaration := core.Find(symbol.Declarations(), ast.IsTypeOrJSTypeAliasDeclaration) typeNode := declaration.Type() t := c.getTypeFromTypeNode(typeNode) @@ -25009,8 +26846,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[CacheHashKey]*Type) - links.instantiations[getTypeListKey(typeParameters)] = t + links.instantiations.intern(getTypeListKey(typeParameters), t) } if t == c.intrinsicMarkerType && symbol.Name() == "BuiltinIteratorReturn" { t = c.getBuiltinIteratorReturnType() @@ -25023,16 +26859,18 @@ func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type { c.error(errorNode, diagnostics.Type_alias_0_circularly_references_itself, c.symbolToString(symbol)) t = c.errorType } - if links.declaredType == nil { - links.declaredType = t + nodeReports := c.endCurrentNodeErrors(reports) + if links.declaredType.peek() == nil { + links.declaredType.setReported(c, t, nodeReports) } + return links.declaredType.peek() } - return links.declaredType + return links.declaredType.get(c) } func (c *Checker) getDeclaredTypeOfEnum(symbol *ast.Symbol) *Type { links := c.declaredTypeLinks.Get(symbol) - if !(links.declaredType != nil) { + if !(links.declaredType.get(c) != nil) { var memberTypeList []*Type for _, declaration := range symbol.Declarations() { if declaration.Kind == ast.KindEnumDeclaration { @@ -25046,7 +26884,7 @@ func (c *Checker) getDeclaredTypeOfEnum(symbol *ast.Symbol) *Type { } else { memberType = c.createComputedEnumType(memberSymbol) } - c.declaredTypeLinks.Get(memberSymbol).declaredType = c.getFreshTypeOfLiteralType(memberType) + c.declaredTypeLinks.Get(memberSymbol).declaredType.setIdentity(c, c.getFreshTypeOfLiteralType(memberType)) memberTypeList = append(memberTypeList, memberType) } } @@ -25062,9 +26900,9 @@ func (c *Checker) getDeclaredTypeOfEnum(symbol *ast.Symbol) *Type { enumType.flags |= TypeFlagsEnumLiteral enumType.symbol = symbol } - links.declaredType = enumType + links.declaredType.setIdentity(c, enumType) } - return links.declaredType + return links.declaredType.get(c) } func (c *Checker) getEnumMemberValue(node *ast.Node) evaluator.Result { @@ -25084,19 +26922,25 @@ func (c *Checker) createComputedEnumType(symbol *ast.Symbol) *Type { func (c *Checker) getDeclaredTypeOfEnumMember(symbol *ast.Symbol) *Type { links := c.declaredTypeLinks.Get(symbol) - if !(links.declaredType != nil) { + if !(links.declaredType.get(c) != nil) { enumType := c.getDeclaredTypeOfEnum(c.getParentOfSymbol(symbol)) - if links.declaredType == nil { - links.declaredType = enumType + if links.declaredType.get(c) == nil { + links.declaredType.setIdentity(c, enumType) } } - return links.declaredType + return links.declaredType.get(c) } func (c *Checker) computeEnumMemberValues(node *ast.Node) { + c.beginMemo(memoKeyOf(node, memoEnumMemberValue)) + c.computeEnumMemberValuesUncached(node) + c.endMemo() +} + +func (c *Checker) computeEnumMemberValuesUncached(node *ast.Node) { nodeLinks := c.nodeLinks.Get(node) - if !(nodeLinks.flags&NodeCheckFlagsEnumValuesComputed != 0) { - nodeLinks.flags |= NodeCheckFlagsEnumValuesComputed + if nodeLinks.flags&NodeCheckFlagsEnumValuesComputed == 0 { + setFlagMemoBits(c, memoKeyOf(node, memoEnumMemberValue), &nodeLinks.flags, NodeCheckFlagsEnumValuesComputed, NodeCheckFlagsEnumValuesComputed) autoValue := new(jsnum.Number) var previous *ast.Node for _, member := range node.Members() { @@ -25249,36 +27093,37 @@ func (c *Checker) evaluateEnumMember(expr *ast.Node, symbol *ast.Symbol, locatio func (c *Checker) getDeclaredTypeOfAlias(symbol *ast.Symbol) *Type { links := c.declaredTypeLinks.Get(symbol) - if links.declaredType == nil { - links.declaredType = c.getDeclaredTypeOfSymbol(c.resolveAlias(symbol)) + if links.declaredType.get(c) == nil { + links.declaredType.set(c, c.getDeclaredTypeOfSymbol(c.resolveAlias(symbol))) } - return links.declaredType + return links.declaredType.get(c) } func (c *Checker) getTypeFromTypeQueryNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { // TypeScript 1.0 spec (April 2014): 3.6.3 // The expression is processed as an identifier expression (section 4.3) // or property access expression(section 4.10), // the widened type(section 3.9) of which becomes the result. t := c.checkExpressionWithTypeArguments(node) - links.resolvedType = c.getRegularTypeOfLiteralType(c.getWidenedType(t)) + links.resolvedType.set(c, c.getRegularTypeOfLiteralType(c.getWidenedType(t))) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromArrayOrTupleTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { target := c.getArrayOrTupleTargetType(node) if target == c.emptyGenericType { - links.resolvedType = c.emptyObjectType + links.resolvedType.set(c, c.emptyObjectType) } else if !(node.Kind == ast.KindTupleType && core.Some(node.Elements(), c.isVariadicTupleElement)) && c.isDeferredTypeReferenceNode(node, false) { if node.Kind == ast.KindTupleType && len(node.Elements()) == 0 { - links.resolvedType = target + links.resolvedType.set(c, target) } else { - links.resolvedType = c.createDeferredTypeReference(target, node, nil /*mapper*/, nil /*alias*/) + // The deferred reference is identified by the node, so it outlives a cycle. + links.resolvedType.setIdentity(c, c.createDeferredTypeReference(target, node, nil /*mapper*/, nil /*alias*/)) } } else { var elementTypes []*Type @@ -25288,13 +27133,13 @@ func (c *Checker) getTypeFromArrayOrTupleTypeNode(node *ast.Node) *Type { elementTypes = core.Map(node.Elements(), c.getTypeFromTypeNode) } if target.objectFlags&ObjectFlagsTuple != 0 { - links.resolvedType = c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsFromTypeNode) + links.resolvedType.set(c, c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsFromTypeNode)) } else { - links.resolvedType = c.createTypeReferenceEx(target, elementTypes, ObjectFlagsFromTypeNode) + links.resolvedType.set(c, c.createTypeReferenceEx(target, elementTypes, ObjectFlagsFromTypeNode)) } } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) isVariadicTupleElement(node *ast.Node) bool { @@ -25319,14 +27164,14 @@ func (c *Checker) isReadonlyTypeOperator(node *ast.Node) bool { func (c *Checker) getTypeFromNamedTupleTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { if node.AsNamedTupleMember().DotDotDotToken != nil { - links.resolvedType = c.getTypeFromRestTypeNode(node) + links.resolvedType.set(c, c.getTypeFromRestTypeNode(node)) } else { - links.resolvedType = c.addOptionalityEx(c.getTypeFromTypeNode(node.Type()), true /*isProperty*/, node.QuestionToken() != nil) + links.resolvedType.set(c, c.addOptionalityEx(c.getTypeFromTypeNode(node.Type()), true /*isProperty*/, node.QuestionToken() != nil)) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromRestTypeNode(node *ast.Node) *Type { @@ -25364,16 +27209,16 @@ func (c *Checker) getTypeFromOptionalTypeNode(node *ast.Node) *Type { func (c *Checker) getTypeFromUnionTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { alias := c.getAliasForTypeNode(node) - links.resolvedType = c.getUnionTypeEx(core.Map(node.AsUnionTypeNode().Types.Nodes, c.getTypeFromTypeNode), UnionReductionLiteral, alias, nil /*origin*/) + links.resolvedType.set(c, c.getUnionTypeEx(core.Map(node.AsUnionTypeNode().Types.Nodes, c.getTypeFromTypeNode), UnionReductionLiteral, alias, nil /*origin*/)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromIntersectionTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { alias := c.getAliasForTypeNode(node) types := core.Map(node.AsIntersectionTypeNode().Types.Nodes, c.getTypeFromTypeNode) // We perform no supertype reduction for X & {} or {} & X, where X is one of string, number, bigint, @@ -25387,14 +27232,14 @@ func (c *Checker) getTypeFromIntersectionTypeNode(node *ast.Node) *Type { noSupertypeReduction = t.flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 || t.flags&TypeFlagsTemplateLiteral != 0 && c.isPatternLiteralType(t) } } - links.resolvedType = c.getIntersectionTypeEx(types, core.IfElse(noSupertypeReduction, IntersectionFlagsNoSupertypeReduction, 0), alias) + links.resolvedType.set(c, c.getIntersectionTypeEx(types, core.IfElse(noSupertypeReduction, IntersectionFlagsNoSupertypeReduction, 0), alias)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromTemplateTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { spans := node.AsTemplateLiteralTypeNode().TemplateSpans texts := make([]string, len(spans.Nodes)+1) types := make([]*Type, len(spans.Nodes)) @@ -25403,28 +27248,31 @@ func (c *Checker) getTypeFromTemplateTypeNode(node *ast.Node) *Type { texts[i+1] = span.AsTemplateLiteralTypeSpan().Literal.Text() types[i] = c.getTypeFromTypeNode(span.Type()) } - links.resolvedType = c.getTemplateLiteralType(texts, types) + links.resolvedType.set(c, c.getTemplateLiteralType(texts, types)) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromMappedTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { t := c.newObjectType(ObjectFlagsMapped, node.Symbol()) t.AsMappedType().declaration = node.AsMappedTypeNode() t.alias = c.getAliasForTypeNode(node) - links.resolvedType = t - // Eagerly resolve the constraint type which forces an error if the constraint type circularly - // references itself through one or more type aliases. - c.getConstraintTypeFromMappedType(t) - } - return links.resolvedType + links.resolvedType.set(c, t) + } + t := links.resolvedType.get(c) + // Eagerly resolve the constraint type which forces an error if the constraint type circularly + // references itself through one or more type aliases. This happens on every request rather + // than only when the type is created, so its errors do not depend on whether the type was + // created while checking another node. + c.getConstraintTypeFromMappedType(t) + return t } func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { checkType := c.getTypeFromTypeNode(node.AsConditionalTypeNode().CheckType) alias := c.getAliasForTypeNode(node) allOuterTypeParameters := c.getOuterTypeParameters(node, true /*includeThisTypes*/) @@ -25441,16 +27289,14 @@ func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { isDistributive: checkType.flags&TypeFlagsTypeParameter != 0, inferTypeParameters: c.getInferTypeParameters(node), outerTypeParameters: outerTypeParameters, - instantiations: nil, alias: alias, } - links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil) + links.resolvedType.set(c, c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil)) if outerTypeParameters != nil { - root.instantiations = make(map[CacheHashKey]*Type) - root.instantiations[getConditionalTypeKey(outerTypeParameters, nil /*alias*/, false /*forConstraint*/)] = links.resolvedType + root.instantiations.intern(getConditionalTypeKey(outerTypeParameters, nil /*alias*/, false /*forConstraint*/), links.resolvedType.get(c)) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type { @@ -25464,7 +27310,7 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, // cases we increment the tail recursion counter and stop after 1000 iterations. for { if tailCount == 1000 { - c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) + c.errorAtCurrentNode(diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) return c.errorType } checkType := c.instantiateType(c.getActualTypeVariable(root.checkType), mapper) @@ -25637,11 +27483,13 @@ func (c *Checker) getPermissiveInstantiation(t *Type) *Type { return t } key := CachedTypeKey{kind: CachedTypeKindPermissiveInstantiation, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { + if cached, _ := c.cachedTypes.get(c, key); cached != nil { return cached } + c.cachedTypes.begin(c, key) result := c.instantiateType(t, c.permissiveMapper) - c.cachedTypes[key] = result + c.cachedTypes.set(c, key, result) + c.endMemo() return result } @@ -25650,32 +27498,41 @@ func (c *Checker) getRestrictiveInstantiation(t *Type) *Type { return t } key := CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { + if cached, _ := c.cachedTypes.get(c, key); cached != nil { return cached } + c.cachedTypes.begin(c, key) result := c.instantiateType(t, c.restrictiveMapper) - c.cachedTypes[key] = result + c.cachedTypes.set(c, key, result) + c.endMemo() // We set the following so we don't attempt to set the restrictive instance of a restrictive instance // which is redundant - we'll produce new type identities, but all type params have already been mapped. // This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint" // assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters // are constrained to `unknown` and produce tons of false positives/negatives! - c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: result.id}] = result + c.cachedTypes.intern(CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: result.id}, result) return result } +// isRestrictiveInstantiation reports whether t is the result of a restrictive +// instantiation, which is recorded purely from its construction. +func (c *Checker) isRestrictiveInstantiation(t *Type) bool { + r, _ := c.cachedTypes.get(c, CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id}) + return r == t +} + func (c *Checker) getRestrictiveTypeParameter(t *Type) *Type { - if t.AsTypeParameter().constraint == nil && c.getConstraintDeclaration(t) == nil || t.AsTypeParameter().constraint == c.noConstraintType { + if t.AsTypeParameter().constraint.get(c) == nil && c.getConstraintDeclaration(t) == nil || t.AsTypeParameter().constraint.get(c) == c.noConstraintType { return t } key := CachedTypeKey{kind: CachedTypeKindRestrictiveTypeParameter, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { + if cached, _ := c.cachedTypes.get(c, key); cached != nil { return cached } result := c.newTypeParameter(t.symbol) - result.AsTypeParameter().constraint = c.noConstraintType + result.AsTypeParameter().constraint.intern(c.noConstraintType) result.AsTypeParameter().origin = t - c.cachedTypes[key] = result + c.cachedTypes.intern(key, result) return result } @@ -25695,57 +27552,53 @@ func (c *Checker) permissiveMapperWorker(t *Type) *Type { func (c *Checker) getTrueTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedTrueType == nil { - d.resolvedTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.mapper) - } - return d.resolvedTrueType + return c.memoCompute(&d.resolvedTrueType, func() *Type { + return c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.mapper) + }) } func (c *Checker) getFalseTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedFalseType == nil { - d.resolvedFalseType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.FalseType), d.mapper) - } - return d.resolvedFalseType + return c.memoCompute(&d.resolvedFalseType, func() *Type { + return c.instantiateType(c.getTypeFromTypeNode(d.root.node.FalseType), d.mapper) + }) } func (c *Checker) getInferredTrueTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() - if d.resolvedInferredTrueType == nil { + return c.memoCompute(&d.resolvedInferredTrueType, func() *Type { if d.combinedMapper != nil { - d.resolvedInferredTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.combinedMapper) - } else { - d.resolvedInferredTrueType = c.getTrueTypeFromConditionalType(t) + return c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.combinedMapper) } - } - return d.resolvedInferredTrueType + return c.getTrueTypeFromConditionalType(t) + }) } func (c *Checker) getTypeFromInferTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - links.resolvedType = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node.AsInferTypeNode().TypeParameter)) + if links.resolvedType.get(c) == nil { + links.resolvedType.set(c, c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node.AsInferTypeNode().TypeParameter))) } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getTypeFromImportTypeNode(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { n := node.AsImportTypeNode() if !ast.IsLiteralImportTypeNode(node) { c.error(n.Argument, diagnostics.String_literal_expected) - c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol - links.resolvedType = c.errorType - return links.resolvedType + c.symbolNodeLinks.Get(node).resolvedSymbol.set(c, c.unknownSymbol) + links.resolvedType.set(c, c.errorType) + return links.resolvedType.get(c) } targetMeaning := core.IfElse(n.IsTypeOf, ast.SymbolFlagsValue, ast.SymbolFlagsType) // TODO: Future work: support unions/generics/whatever via a deferred import-type innerModuleSymbol := c.resolveExternalModuleName(node, n.Argument.AsLiteralTypeNode().Literal, false /*ignoreErrors*/, c.getTypeFromImportAttributes(ast.GetImportAttributes(node))) if innerModuleSymbol == nil { - c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol - links.resolvedType = c.errorType - return links.resolvedType + c.symbolNodeLinks.Get(node).resolvedSymbol.set(c, c.unknownSymbol) + links.resolvedType.set(c, c.errorType) + return links.resolvedType.get(c) } moduleSymbol := c.resolveExternalModuleSymbol(innerModuleSymbol, false /*dontResolveAlias*/) if !ast.NodeIsMissing(n.Qualifier) { @@ -25780,28 +27633,28 @@ func (c *Checker) getTypeFromImportTypeNode(node *ast.Node) *Type { next := core.OrElse(symbolFromModule, symbolFromVariable) if next == nil { c.error(current, diagnostics.Namespace_0_has_no_exported_member_1, c.getFullyQualifiedName(currentNamespace, nil), scanner.DeclarationNameToString(current)) - links.resolvedType = c.errorType - return links.resolvedType + links.resolvedType.set(c, c.errorType) + return links.resolvedType.get(c) } - c.symbolNodeLinks.Get(current).resolvedSymbol = next - c.symbolNodeLinks.Get(current.Parent).resolvedSymbol = next + c.symbolNodeLinks.Get(current).resolvedSymbol.set(c, next) + c.symbolNodeLinks.Get(current.Parent).resolvedSymbol.set(c, next) currentNamespace = next } - links.resolvedType = c.resolveImportSymbolType(node, currentNamespace, targetMeaning) + links.resolvedType.set(c, c.resolveImportSymbolType(node, currentNamespace, targetMeaning)) } else { if c.getSymbolFlags(moduleSymbol)&targetMeaning != 0 { - links.resolvedType = c.resolveImportSymbolType(node, moduleSymbol, targetMeaning) + links.resolvedType.set(c, c.resolveImportSymbolType(node, moduleSymbol, targetMeaning)) } else { message := core.IfElse(targetMeaning == ast.SymbolFlagsValue, diagnostics.Module_0_does_not_refer_to_a_value_but_is_used_as_a_value_here, diagnostics.Module_0_does_not_refer_to_a_type_but_is_used_as_a_type_here_Did_you_mean_typeof_import_0) c.error(node, message, n.Argument.AsLiteralTypeNode().Literal.Text()) - c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol - links.resolvedType = c.errorType + c.symbolNodeLinks.Get(node).resolvedSymbol.set(c, c.unknownSymbol) + links.resolvedType.set(c, c.errorType) } } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) getIdentifierChain(node *ast.Node) []*ast.Node { @@ -25813,7 +27666,7 @@ func (c *Checker) getIdentifierChain(node *ast.Node) []*ast.Node { func (c *Checker) resolveImportSymbolType(node *ast.Node, symbol *ast.Symbol, meaning ast.SymbolFlags) *Type { resolvedSymbol := c.resolveSymbol(symbol) - c.symbolNodeLinks.Get(node).resolvedSymbol = resolvedSymbol + c.symbolNodeLinks.Get(node).resolvedSymbol.set(c, resolvedSymbol) if meaning == ast.SymbolFlagsValue { // intentionally doesn't use resolved symbol so type is cached as expected on the alias return c.getInstantiationExpressionType(c.getTypeOfSymbol(symbol), node) @@ -25837,18 +27690,22 @@ func (c *Checker) getGlobalStrictFunctionType(name string) *Type { } func (c *Checker) getGlobalImportMetaExpressionType() *Type { - if c.deferredGlobalImportMetaExpressionType == nil { - // Create a synthetic type `ImportMetaExpression { meta: MetaProperty }` - symbol := c.newSymbol(ast.SymbolFlagsNone, "ImportMetaExpression") - importMetaType := c.getGlobalImportMetaType() - metaPropertySymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "meta", ast.CheckFlagsReadonly) - metaPropertySymbol.SetParent(symbol) - c.valueSymbolLinks.Get(metaPropertySymbol).resolvedType = importMetaType - members := createSymbolTable([]*ast.Symbol{metaPropertySymbol}) - symbol.SetMembers(members, &c.symbolExtraArena) - c.deferredGlobalImportMetaExpressionType = c.newAnonymousType(symbol, members, nil, nil, nil) + if t := c.deferredGlobalImportMetaExpressionType.get(c); t != nil { + return t } - return c.deferredGlobalImportMetaExpressionType + c.deferredGlobalImportMetaExpressionType.begin(c) + // Create a synthetic type `ImportMetaExpression { meta: MetaProperty }` + symbol := c.newSymbol(ast.SymbolFlagsNone, "ImportMetaExpression") + importMetaType := c.getGlobalImportMetaType() + metaPropertySymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "meta", ast.CheckFlagsReadonly) + metaPropertySymbol.SetParent(symbol) + c.valueSymbolLinks.Get(metaPropertySymbol).resolvedType.intern(importMetaType) + members := createSymbolTable([]*ast.Symbol{metaPropertySymbol}) + symbol.SetMembers(members, &c.symbolExtraArena) + t := c.newAnonymousType(symbol, members, nil, nil, nil) + c.deferredGlobalImportMetaExpressionType.set(c, t) + c.endMemo() + return t } func (c *Checker) createIterableType(iteratedType *Type) *Type { @@ -25914,10 +27771,10 @@ func (c *Checker) getTupleTargetType(elementInfos []TupleElementInfo, readonly b return c.globalArrayType } key := getTupleKey(elementInfos, readonly) - t := c.tupleTypes[key] + t, _ := c.tupleTypes.get(key) if t == nil { t = c.createTupleTargetType(elementInfos, readonly) - c.tupleTypes[key] = t + c.tupleTypes.intern(key, t) } return t } @@ -25946,7 +27803,7 @@ func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonl combinedFlags |= flags if combinedFlags&ElementFlagsVariable == 0 { property := c.newSymbolEx(ast.SymbolFlagsProperty|core.IfElse(flags&ElementFlagsOptional != 0, ast.SymbolFlagsOptional, 0), strconv.Itoa(i), core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) - c.valueSymbolLinks.Get(property).resolvedType = typeParameter + c.valueSymbolLinks.Get(property).resolvedType.intern(typeParameter) // c.valueSymbolLinks.get(property).tupleLabelDeclaration = elementInfos[i].labeledDeclaration members.Set(property.Name(), property) } @@ -25955,28 +27812,27 @@ func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonl fixedLength := members.Len() lengthSymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "length", core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) if combinedFlags&ElementFlagsVariable != 0 { - c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.numberType + c.valueSymbolLinks.Get(lengthSymbol).resolvedType.intern(c.numberType) } else { var literalTypes []*Type for i := minLength; i <= arity; i++ { literalTypes = append(literalTypes, c.getNumberLiteralType(jsnum.Number(i))) } - c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.getUnionType(literalTypes) + c.valueSymbolLinks.Get(lengthSymbol).resolvedType.intern(c.getUnionType(literalTypes)) } members.Set(lengthSymbol.Name(), lengthSymbol) t := c.newObjectType(ObjectFlagsTuple|ObjectFlagsReference, nil) d := t.AsTupleType() d.thisType = c.newTypeParameter(nil) d.thisType.AsTypeParameter().isThisType = true - d.thisType.AsTypeParameter().constraint = t + d.thisType.AsTypeParameter().constraint.intern(t) for _, tp := range typeParameters { tp.AsTypeParameter().origin = t } d.allTypeParameters = append(typeParameters, d.thisType) - d.instantiations = make(map[CacheHashKey]*Type) - d.instantiations[getTypeListKey(d.TypeParameters())] = t + d.instantiations.intern(getTypeListKey(d.TypeParameters()), t) d.target = t - d.resolvedTypeArguments = d.TypeParameters() + d.resolvedTypeArguments.intern(d.TypeParameters()) d.declaredMembersResolved = true d.declaredMembers = members d.elementInfos = elementInfos @@ -26040,17 +27896,20 @@ func (c *Checker) isGenericIndexType(t *Type) bool { func (c *Checker) getGenericObjectFlags(t *Type) ObjectFlags { var combinedFlags ObjectFlags if t.flags&(TypeFlagsUnionOrIntersection|TypeFlagsSubstitution) != 0 { - if t.objectFlags&ObjectFlagsIsGenericTypeComputed == 0 { + if c.objectFlagsKnown(t, ObjectFlagsIsGenericTypeComputed) { + return t.objectFlags & ObjectFlagsIsGenericType + } + return c.objectFlagsCell(t, memoGenericObjectFlags, ObjectFlagsIsGenericTypeComputed, ObjectFlagsIsGenericType, false /*inProgress*/, func() ObjectFlags { + var flags ObjectFlags if t.flags&TypeFlagsUnionOrIntersection != 0 { for _, u := range t.Types() { - combinedFlags |= c.getGenericObjectFlags(u) + flags |= c.getGenericObjectFlags(u) } } else { - combinedFlags = c.getGenericObjectFlags(t.AsSubstitutionType().baseType) | c.getGenericObjectFlags(t.AsSubstitutionType().constraint) + flags = c.getGenericObjectFlags(t.AsSubstitutionType().baseType) | c.getGenericObjectFlags(t.AsSubstitutionType().constraint) } - t.objectFlags |= ObjectFlagsIsGenericTypeComputed | combinedFlags - } - return t.objectFlags & ObjectFlagsIsGenericType + return flags + }) } if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || c.isGenericMappedType(t) || c.isGenericTupleType(t) { combinedFlags |= ObjectFlagsIsGenericObjectType @@ -26096,10 +27955,8 @@ func (c *Checker) isGenericReducibleType(t *Type) bool { func (c *Checker) isReducibleIntersection(t *Type) bool { d := t.AsIntersectionType() - if d.uniqueLiteralFilledInstantiation == nil { - d.uniqueLiteralFilledInstantiation = c.instantiateType(t, c.uniqueLiteralMapper) - } - return c.getReducedType(d.uniqueLiteralFilledInstantiation) != d.uniqueLiteralFilledInstantiation + instantiated := c.memoCompute(&d.uniqueLiteralFilledInstantiation, func() *Type { return c.instantiateType(t, c.uniqueLiteralMapper) }) + return c.getReducedType(instantiated) != instantiated } func (c *Checker) getUniqueLiteralTypeForTypeParameter(t *Type) *Type { @@ -26166,7 +28023,7 @@ func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, t *Type, kin t.objectFlags = objectFlags &^ (ObjectFlagsCouldContainTypeVariablesComputed | ObjectFlagsCouldContainTypeVariables | ObjectFlagsMembersResolved) t.id = TypeId(c.TypeCount) t.kind = kind //nolint:customlint - t.checker = c + t.checker = c.root if c.tracer != nil { c.tracer.RecordType(t) } @@ -26275,14 +28132,14 @@ func (c *Checker) createTypeReference(target *Type, typeArguments []*Type) *Type func (c *Checker) createTypeReferenceEx(target *Type, typeArguments []*Type, objectFlags ObjectFlags) *Type { id := getTypeListKey(typeArguments) intf := target.AsInterfaceType() - if t, ok := intf.instantiations[id]; ok { + if t, ok := intf.instantiations.get(c, id); ok { return t } t := c.newObjectType(ObjectFlagsReference|objectFlags|c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone), target.symbol) d := t.AsTypeReference() d.target = target - d.resolvedTypeArguments = typeArguments - intf.instantiations[id] = t + d.resolvedTypeArguments.intern(typeArguments) + intf.instantiations.intern(id, t) return t } @@ -26306,12 +28163,18 @@ func (c *Checker) cloneTypeReference(source *Type) *Type { t := c.newObjectType(ObjectFlagsReference, source.symbol) t.objectFlags = source.objectFlags &^ ObjectFlagsMembersResolved t.AsTypeReference().target = source.AsTypeReference().target - t.AsTypeReference().resolvedTypeArguments = source.AsTypeReference().resolvedTypeArguments + t.AsTypeReference().resolvedTypeArguments.intern(source.AsTypeReference().resolvedTypeArguments.peek()) return t } +// setStructuredTypeMembers sets the members of a type being created, which +// are then resolved. func (c *Checker) setStructuredTypeMembers(t *Type, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { t.objectFlags |= ObjectFlagsMembersResolved + c.setStructuredTypeData(t, members, callSignatures, constructSignatures, indexInfos) +} + +func (c *Checker) setStructuredTypeData(t *Type, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { data := t.AsStructuredType() data.members = members data.properties = c.getNamedMembers(members) @@ -26429,10 +28292,10 @@ func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, type sig.setTypeParameters(c, typeParameters) sig.parameters = parameters sig.setThisParameter(c, thisParameter) - sig.resolvedReturnType = resolvedReturnType - sig.setResolvedTypePredicate(c, resolvedTypePredicate) + sig.resolvedReturnType.intern(resolvedReturnType) + sig.initResolvedTypePredicate(c, resolvedTypePredicate) sig.minArgumentCount = int32(minArgumentCount) - sig.resolvedMinArgumentCount = -1 + sig.resolvedMinArgumentCount.intern(-1) return sig } @@ -26479,10 +28342,10 @@ func isFreshLiteralType(t *Type) bool { } func (c *Checker) getStringLiteralType(value string) *Type { - t := c.stringLiteralTypes[value] + t, _ := c.stringLiteralTypes.get(value) if t == nil { t = c.newLiteralType(TypeFlagsStringLiteral, value, nil) - c.stringLiteralTypes[value] = t + c.stringLiteralTypes.intern(value, t) } return t } @@ -26496,19 +28359,19 @@ func (c *Checker) getNumberLiteralType(value jsnum.Number) *Type { } return c.nanType } - t := c.numberLiteralTypes[value] + t, _ := c.numberLiteralTypes.get(value) if t == nil { t = c.newLiteralType(TypeFlagsNumberLiteral, value, nil) - c.numberLiteralTypes[value] = t + c.numberLiteralTypes.intern(value, t) } return t } func (c *Checker) getBigIntLiteralType(value jsnum.PseudoBigInt) *Type { - t := c.bigintLiteralTypes[value] + t, _ := c.bigintLiteralTypes.get(value) if t == nil { t = c.newLiteralType(TypeFlagsBigIntLiteral, value, nil) - c.bigintLiteralTypes[value] = t + c.bigintLiteralTypes.intern(value, t) } return t } @@ -26545,11 +28408,11 @@ func (c *Checker) getEnumLiteralType(value any, enumSymbol *ast.Symbol, symbol * // NaN cannot be used as a Go map key because NaN != NaN in IEEE 754, // so Go map lookups for NaN always miss. Cache NaN enum types separately by enum symbol. if v.IsNaN() { - t := c.enumNaNLiteralTypes[enumSymbol] + t, _ := c.enumNaNLiteralTypes.get(enumSymbol) if t == nil { t = c.newLiteralType(flags, value, nil) t.symbol = symbol - c.enumNaNLiteralTypes[enumSymbol] = t + c.enumNaNLiteralTypes.intern(enumSymbol, t) } return t } @@ -26557,11 +28420,11 @@ func (c *Checker) getEnumLiteralType(value any, enumSymbol *ast.Symbol, symbol * panic("Unhandled case in getEnumLiteralType") } key := EnumLiteralKey{enumSymbol: enumSymbol, value: value} - t := c.enumLiteralTypes[key] + t, _ := c.enumLiteralTypes.get(key) if t == nil { t = c.newLiteralType(flags, value, nil) t.symbol = symbol - c.enumLiteralTypes[key] = t + c.enumLiteralTypes.intern(key, t) } return t } @@ -26652,11 +28515,13 @@ func (c *Checker) getBaseTypeOfEnumLikeType(t *Type) *Type { func (c *Checker) getBaseTypeOfLiteralTypeUnion(t *Type) *Type { key := CachedTypeKey{kind: CachedTypeKindLiteralUnionBaseType, typeId: t.id} - if cached, ok := c.cachedTypes[key]; ok { + if cached, ok := c.cachedTypes.get(c, key); ok { return cached } + c.cachedTypes.begin(c, key) result := c.mapType(t, c.getBaseTypeOfLiteralType) - c.cachedTypes[key] = result + c.cachedTypes.set(c, key, result) + c.endMemo() return result } @@ -26826,10 +28691,12 @@ func (c *Checker) getUnionTypeEx(types []*Type, unionReduction UnionReduction, a id1, id2 = id2, id1 } key := UnionOfUnionKey{id1: id1, id2: id2, r: unionReduction, a: getAliasKey(alias)} - t := c.unionOfUnionTypes[key] + t, _ := c.unionOfUnionTypes.get(c, key) if t == nil { + c.unionOfUnionTypes.begin(c, key) t = c.getUnionTypeWorker(types, unionReduction, alias, nil /*origin*/) - c.unionOfUnionTypes[key] = t + c.unionOfUnionTypes.set(c, key, t) + c.endMemo() } return t } @@ -26930,7 +28797,7 @@ func (c *Checker) getUnionTypeFromSortedList(types []*Type, precomputedObjectFla return types[0] } key := getUnionKey(types, origin, alias) - t := c.unionTypes[key] + t, _ := c.unionTypes.get(key) if t == nil { t = c.newUnionType(precomputedObjectFlags|c.getPropagatingFlagsOfTypes(types, TypeFlagsNullable), types) t.AsUnionType().origin = origin @@ -26938,13 +28805,13 @@ func (c *Checker) getUnionTypeFromSortedList(types []*Type, precomputedObjectFla if len(types) == 2 && types[0].flags&TypeFlagsBooleanLiteral != 0 && types[1].flags&TypeFlagsBooleanLiteral != 0 { t.flags |= TypeFlagsBoolean } - c.unionTypes[key] = t + c.unionTypes.intern(key, t) } return t } func (c *Checker) UnionTypes() iter.Seq[*Type] { - return maps.Values(c.unionTypes) + return c.unionTypes.values() } func (c *Checker) addTypesToUnion(sourceTypes []*Type, types []*Type) ([]*Type, TypeFlags) { @@ -27125,9 +28992,10 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { return types } key := getTypeListKey(types) - if cached := c.subtypeReductionCache[key]; cached != nil { + if cached, _ := c.subtypeReductionCache.get(c, key); cached != nil { return cached } + c.subtypeReductionCache.begin(c, key) // We assume that redundant primitive types have already been removed from the types array and that there // are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty // object types, and if none of those are present we can exclude primitive types from the subtype check. @@ -27181,7 +29049,8 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { if tr := c.tracer; tr != nil { tr.Instant(tracing.PhaseCheckTypes, "removeSubtypes_DepthLimit", map[string]any{"estimatedCount": estimatedCount}) } - c.error(c.currentNode, diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent) + c.errorAtCurrentNode(diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent) + c.endMemo() return nil } } @@ -27206,7 +29075,8 @@ func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { } } reduced := c.typeListArena.Clone(types) - c.subtypeReductionCache[key] = reduced + c.subtypeReductionCache.set(c, key, reduced) + c.endMemo() return reduced } @@ -27353,8 +29223,9 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, } } key := getIntersectionKey(typeSet, flags, alias) - result := c.intersectionTypes[key] + result, _ := c.intersectionTypes.get(c, key) if result == nil { + c.intersectionTypes.begin(c, key) if includes&TypeFlagsUnion != 0 { var reduced bool typeSet, reduced = c.intersectUnionsOfPrimitiveTypes(typeSet) @@ -27390,6 +29261,7 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, // the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type // exceeds 100000 constituents, report an error. if !c.checkCrossProductUnion(typeSet) { + c.endMemo() return c.errorType } constituents := c.getCrossProductIntersections(typeSet, flags) @@ -27405,7 +29277,8 @@ func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, } else { result = c.getIntersectionTypeWithContent(objectFlags|c.getPropagatingFlagsOfTypes(types /*excludeKinds*/, TypeFlagsNullable), typeSet, alias) } - c.intersectionTypes[key] = result + c.intersectionTypes.setIdentity(c, key, result) + c.endMemo() } return result } @@ -27419,12 +29292,12 @@ func (c *Checker) getIntersectionTypeWithContent(objectFlags ObjectFlags, typeSe b.writeUint32(uint32(objectFlags)) b.writeAlias(alias) key := b.hash() - if existing := c.intersectionContentTypes[key]; existing != nil { + if existing, _ := c.intersectionContentTypes.get(key); existing != nil { return existing } result := c.newIntersectionType(objectFlags, typeSet) result.alias = alias - c.intersectionContentTypes[key] = result + c.intersectionContentTypes.intern(key, result) return result } @@ -27861,7 +29734,7 @@ func (c *Checker) checkCrossProductUnion(types []*Type) bool { if tr := c.tracer; tr != nil { tr.Instant(tracing.PhaseCheckTypes, "checkCrossProductUnion_DepthLimit", map[string]any{"size": size}) } - c.error(c.currentNode, diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent) + c.errorAtCurrentNode(diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent) return false } return true @@ -27926,9 +29799,10 @@ func (c *Checker) getExtractStringType(t *Type) *Type { func (c *Checker) getLiteralTypeFromProperties(t *Type, include TypeFlags, includeOrigin bool) *Type { key := PropertiesTypesKey{typeId: t.id, include: include, includeOrigin: includeOrigin} - if cached, ok := c.propertiesTypes[key]; ok { + if cached, ok := c.propertiesTypes.get(c, key); ok { return cached } + c.propertiesTypes.begin(c, key) var origin *Type if includeOrigin && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 || t.alias != nil { origin = c.newIndexType(t, IndexFlagsNone) @@ -27949,7 +29823,8 @@ func (c *Checker) getLiteralTypeFromProperties(t *Type, include TypeFlags, inclu } } result := c.getUnionTypeEx(types, UnionReductionLiteral, nil, origin) - c.propertiesTypes[key] = result + c.propertiesTypes.set(c, key, result) + c.endMemo() return result } @@ -28010,30 +29885,37 @@ func isInvalidComputedPropertyName(node *ast.Node) bool { } func (c *Checker) checkComputedPropertyName(node *ast.Node) *Type { + c.typeNodeLinks.Get(node).resolvedType.begin(c) + result := c.checkComputedPropertyNameUncached(node) + c.endMemo() + return result +} + +func (c *Checker) checkComputedPropertyNameUncached(node *ast.Node) *Type { links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { if isInvalidComputedPropertyName(node) { - links.resolvedType = c.errorType - return links.resolvedType + links.resolvedType.set(c, c.errorType) + return links.resolvedType.get(c) } - if !c.pushTypeResolution(node, TypeSystemPropertyNameComputedPropertyName) { + if !c.pushTypeResolution(node, TypeSystemPropertyNameComputedPropertyName, &links.resolvedType.memoHeader) { return c.errorType } t := c.checkExpression(node.Expression()) c.popTypeResolution() - if links.resolvedType != nil { - return links.resolvedType + if links.resolvedType.get(c) != nil { + return links.resolvedType.get(c) } - links.resolvedType = t + links.resolvedType.set(c, t) // This will allow types number, string, symbol or any. It will also allow enums, the unknown // type, and any union of these types (like string | number). - if links.resolvedType.flags&TypeFlagsNullable != 0 || - !c.isTypeAssignableToKind(links.resolvedType, TypeFlagsStringLike|TypeFlagsNumberLike|TypeFlagsESSymbolLike) && - !c.isTypeAssignableTo(links.resolvedType, c.stringNumberSymbolType) { + if links.resolvedType.get(c).flags&TypeFlagsNullable != 0 || + !c.isTypeAssignableToKind(links.resolvedType.get(c), TypeFlagsStringLike|TypeFlagsNumberLike|TypeFlagsESSymbolLike) && + !c.isTypeAssignableTo(links.resolvedType.get(c), c.stringNumberSymbolType) { c.error(node, diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any) } } - return links.resolvedType + return links.resolvedType.get(c) } func (c *Checker) isNoInferType(t *Type) bool { @@ -28072,11 +29954,11 @@ func (c *Checker) getIndexTypeForGenericType(t *Type, indexFlags IndexFlags) *Ty kind: core.IfElse(indexFlags&IndexFlagsStringsOnly != 0, CachedTypeKindStringIndexType, CachedTypeKindIndexType), typeId: t.id, } - if indexType := c.cachedTypes[key]; indexType != nil { + if indexType, _ := c.cachedTypes.get(c, key); indexType != nil { return indexType } indexType := c.newIndexType(t, indexFlags&IndexFlagsStringsOnly) - c.cachedTypes[key] = indexType + c.cachedTypes.intern(key, indexType) return indexType } @@ -28177,11 +30059,11 @@ func (c *Checker) getIndexedAccessTypeOrUndefined(objectType *Type, indexType *T // Defer the operation by creating an indexed access type. persistentAccessFlags := accessFlags & AccessFlagsPersistent key := getIndexedAccessKey(objectType, indexType, persistentAccessFlags, alias) - t := c.indexedAccessTypes[key] + t, _ := c.indexedAccessTypes.get(key) if t == nil { t = c.newIndexedAccessType(objectType, indexType, persistentAccessFlags) t.alias = alias - c.indexedAccessTypes[key] = t + c.indexedAccessTypes.intern(key, t) } return t } @@ -28254,7 +30136,7 @@ func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectTy return nil } if accessFlags&AccessFlagsCacheSymbol != 0 { - c.symbolNodeLinks.Get(accessNode).resolvedSymbol = prop + c.symbolNodeLinks.Get(accessNode).resolvedSymbol.set(c, prop) } if c.isThisPropertyAccessInConstructor(accessExpression, prop) { return c.autoType @@ -28353,7 +30235,7 @@ func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectTy c.addDiagnostic(createDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType))) return c.undefinedType } else if indexType.flags&(TypeFlagsNumber|TypeFlagsString) != 0 { - types := core.Map(objectType.AsStructuredType().getProperties(), func(prop *ast.Symbol) *Type { + types := core.Map(objectType.AsStructuredType().getProperties(c), func(prop *ast.Symbol) *Type { return c.getTypeOfSymbol(prop) }) return c.getUnionType(append(types, c.undefinedType)) @@ -28634,11 +30516,11 @@ func (c *Checker) getSubstitutionType(baseType *Type, constraint *Type) *Type { func (c *Checker) getOrCreateSubstitutionType(baseType *Type, constraint *Type) *Type { key := SubstitutionTypeKey{baseId: baseType.id, constraintId: constraint.id} - if cached := c.substitutionTypes[key]; cached != nil { + if cached, _ := c.substitutionTypes.get(key); cached != nil { return cached } result := c.newSubstitutionType(baseType, constraint) - c.substitutionTypes[key] = result + c.substitutionTypes.intern(key, result) return result } @@ -28662,14 +30544,25 @@ func (c *Checker) getBaseConstraintOfType(t *Type) *Type { } func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type { + if constrained := t.AsConstrainedType(); constrained != nil && constrained.resolvedBaseConstraint.peek() != nil { + // A resolved base constraint is only read. + return constrained.resolvedBaseConstraint.get(c) + } + c.beginMemo(memoKeyOf(t, memoBaseConstraint)) + result := c.getResolvedBaseConstraintUncached(t, stack) + c.endMemo() + return result +} + +func (c *Checker) getResolvedBaseConstraintUncached(t *Type, stack []RecursionId) *Type { constrained := t.AsConstrainedType() if constrained == nil { return t } - if constrained.resolvedBaseConstraint != nil { - return constrained.resolvedBaseConstraint + if constrained.resolvedBaseConstraint.get(c) != nil { + return constrained.resolvedBaseConstraint.get(c) } - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstraint) { + if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstraint, &constrained.resolvedBaseConstraint.memoHeader) { return c.circularConstraintType } var constraint *Type @@ -28679,7 +30572,7 @@ func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type // levels of nesting, we are presumably exploring a repeating pattern with a long cycle that hasn't // yet triggered the deeply nested limiter. We have no test cases that actually get to 50 levels of // nesting, so it is effectively just a safety stop. - identity := getRecursionIdentity(t) + identity := c.getRecursionIdentity(t) if len(stack) < 10 || len(stack) < 50 && !slices.Contains(stack, identity) { constraint = c.computeBaseConstraint(c.getSimplifiedType(t /*writing*/, false), append(stack, identity)) } @@ -28687,10 +30580,21 @@ func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type if t.flags&TypeFlagsTypeParameter != 0 { errorNode := c.getConstraintDeclaration(t) if errorNode != nil { - diagnostic := c.error(errorNode, diagnostics.Type_parameter_0_has_a_circular_constraint, c.TypeToString(t)) - if c.currentNode != nil && !isNodeDescendantOf(errorNode, c.currentNode) && !isNodeDescendantOf(c.currentNode, errorNode) { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(c.currentNode, diagnostics.Circularity_originates_in_type_at_this_location)) - } + typeString := c.TypeToString(t) + // The error is located at the constraint, not the current node, and the + // circularity is intrinsic to the declarations, so it is reported once + // and the circular result is kept. Discarding it would let a later + // computation reuse cycle participants that hide the cycle. + // Only the cycle of an instantiation originates at a use; that of a + // declared type parameter is found by checking its declaration, and + // whichever use happens to find it first is no origin. + instantiated := t.AsTypeParameter().target != nil + c.reportAtCurrentNodeOnce(func(c *Checker, node *ast.Node) { + diagnostic := c.error(errorNode, diagnostics.Type_parameter_0_has_a_circular_constraint, typeString) + if instantiated && node != nil && !isNodeDescendantOf(errorNode, node) && !isNodeDescendantOf(node, errorNode) { + diagnostic.AddRelatedInfo(NewDiagnosticForNode(node, diagnostics.Circularity_originates_in_type_at_this_location)) + } + }) } } constraint = c.circularConstraintType @@ -28698,8 +30602,8 @@ func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type if constraint == nil { constraint = c.noConstraintType } - if constrained.resolvedBaseConstraint == nil { - constrained.resolvedBaseConstraint = constraint + if constrained.resolvedBaseConstraint.get(c) == nil { + constrained.resolvedBaseConstraint.set(c, constraint) } return constraint } @@ -28777,12 +30681,12 @@ func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type { } return c.getNextBaseConstraint(c.getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, t.AsIndexedAccessType().accessFlags, nil, nil), stack) case t.flags&TypeFlagsConditional != 0: - if c.conditionalConstraintDepth >= 100 { + if c.depthLimitReached(&c.conditionalConstraintDepth, 100) { return nil } - c.conditionalConstraintDepth++ + c.enterDepthLimit(&c.conditionalConstraintDepth) constraint := c.getConstraintFromConditionalType(t) - c.conditionalConstraintDepth-- + c.conditionalConstraintDepth.exit() return c.getNextBaseConstraint(constraint, stack) case t.flags&TypeFlagsSubstitution != 0: return c.getNextBaseConstraint(c.getSubstitutionIntersection(t), stack) @@ -28943,7 +30847,7 @@ func (c *Checker) markPropertyAsReferenced(prop *ast.Symbol, nodeForCheckWriteOn if prop.CheckFlags()&ast.CheckFlagsInstantiated != 0 { target = c.valueSymbolLinks.Get(prop).target } - c.symbolReferenceLinks.Get(target).referenceKinds |= ast.SymbolFlagsAll + c.symbolReferenced(target, ast.SymbolFlagsAll) } func (c *Checker) expandSignatureParametersWithTupleMembers(signature *Signature, restType *TypeReference, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol { @@ -28963,9 +30867,9 @@ func (c *Checker) expandSignatureParametersWithTupleMembers(signature *Signature symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, associatedNames[i], checkFlags) links := c.valueSymbolLinks.Get(symbol) if flags&ElementFlagsRest != 0 { - links.resolvedType = c.createArrayType(t) + links.resolvedType.setIdentity(c, c.createArrayType(t)) } else { - links.resolvedType = t + links.resolvedType.setIdentity(c, t) } expanded = append(expanded, symbol) } @@ -29019,14 +30923,16 @@ func getNameFromIndexInfo(info *IndexInfo) string { func (c *Checker) isUnknownLikeUnionType(t *Type) bool { if c.strictNullChecks && t.flags&TypeFlagsUnion != 0 { - if t.objectFlags&ObjectFlagsIsUnknownLikeUnionComputed == 0 { - t.objectFlags |= ObjectFlagsIsUnknownLikeUnionComputed + if c.objectFlagsKnown(t, ObjectFlagsIsUnknownLikeUnionComputed) { + return t.objectFlags&ObjectFlagsIsUnknownLikeUnion != 0 + } + return c.objectFlagsCell(t, memoUnknownLikeUnion, ObjectFlagsIsUnknownLikeUnionComputed, ObjectFlagsIsUnknownLikeUnion, true /*inProgress*/, func() ObjectFlags { types := t.Types() if len(types) >= 3 && types[0].flags&TypeFlagsUndefined != 0 && types[1].flags&TypeFlagsNull != 0 && core.Some(types, c.IsEmptyAnonymousObjectType) { - t.objectFlags |= ObjectFlagsIsUnknownLikeUnion + return ObjectFlagsIsUnknownLikeUnion } - } - return t.objectFlags&ObjectFlagsIsUnknownLikeUnion != 0 + return 0 + }) != 0 } return false } @@ -29141,16 +31047,24 @@ func (c *Checker) getSimplifiedIndexType(t *Type) *Type { // the target of an assignment. func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type { key := CachedTypeKey{kind: core.IfElse(writing, CachedTypeKindIndexedAccessForWriting, CachedTypeKindIndexedAccessForReading), typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return core.IfElse(cached == c.circularConstraintType, t, cached) + if cached, _ := c.cachedTypes.get(c, key); cached != nil { + return cached } - c.cachedTypes[key] = t + propertyName := core.IfElse(writing, TypeSystemPropertyNameSimplifiedForWriting, TypeSystemPropertyNameSimplifiedForReading) + if c.isResolvingAnywhere(t, propertyName) { + // Resolve circular references to t itself while simplifying. + return t + } + c.cachedTypes.begin(c, key) + c.beginTypeResolution(t, propertyName, nil) result := c.getSimplifiedIndexedAccessTypeWorker(t, writing) if result != t { // If the simplification is a union type that includes t, remove t from the type. result = c.removeType(result, t) - c.cachedTypes[key] = result } + c.cachedTypes.set(c, key, result) + c.popTypeResolution() + c.endMemo() return result } @@ -29232,11 +31146,13 @@ func (c *Checker) distributeIndexOverObjectType(objectType *Type, indexType *Typ func (c *Checker) getSimplifiedConditionalType(t *Type, writing bool) *Type { key := CachedTypeKey{kind: core.IfElse(writing, CachedTypeKindSimplifiedConditionalTypeForWriting, CachedTypeKindSimplifiedConditionalTypeForReading), typeId: t.id} - if cached, ok := c.cachedTypes[key]; ok { + if cached, ok := c.cachedTypes.get(c, key); ok { return cached } + c.cachedTypes.begin(c, key) result := c.getSimplifiedConditionalTypeWorker(t, writing) - c.cachedTypes[key] = result + c.cachedTypes.set(c, key, result) + c.endMemo() return result } @@ -29326,10 +31242,17 @@ func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type { return nil } key := CachedTypeKey{kind: CachedTypeKindEquivalentBaseType, typeId: t.id} - if t.objectFlags&ObjectFlagsIdenticalBaseTypeCalculated != 0 { - return c.cachedTypes[key] + if cached, ok := c.cachedTypes.get(c, key); ok { + return cached } - t.objectFlags |= ObjectFlagsIdenticalBaseTypeCalculated + c.cachedTypes.begin(c, key) + result := c.getSingleBaseForNonAugmentingSubtypeUncached(t) + c.cachedTypes.set(c, key, result) + c.endMemo() + return result +} + +func (c *Checker) getSingleBaseForNonAugmentingSubtypeUncached(t *Type) *Type { target := t.Target() if target.objectFlags&ObjectFlagsClass != 0 { baseTypeNode := getBaseTypeNodeOfClass(target) @@ -29357,36 +31280,32 @@ func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type { if len(c.getTypeArguments(t)) > len(typeParameters) { instantiatedBase = c.getTypeWithThisArgument(instantiatedBase, core.LastOrNil(c.getTypeArguments(t)), false) } - c.cachedTypes[key] = instantiatedBase return instantiatedBase } func (c *Checker) getModifiersTypeFromMappedType(t *Type) *Type { m := t.AsMappedType() - if m.modifiersType == nil { + return c.memoCompute(&m.modifiersType, func() *Type { if c.isMappedTypeWithKeyofConstraintDeclaration(t) { // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves // 'keyof T' to a literal union type and we can't recover T from that type. - m.modifiersType = c.instantiateType(c.getTypeFromTypeNode(c.getConstraintDeclarationForMappedType(t).Type()), m.mapper) - } else { - // Otherwise, get the declared constraint type, and if the constraint type is a type parameter, - // get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T', - // the modifiers type is T. Otherwise, the modifiers type is unknown. - declaredType := c.getTypeFromMappedTypeNode(m.declaration.AsNode()) - constraint := c.getConstraintTypeFromMappedType(declaredType) - extendedConstraint := constraint - if constraint != nil && constraint.flags&TypeFlagsTypeParameter != 0 { - extendedConstraint = c.getConstraintOfTypeParameter(getNonDistributedTypeParameter(constraint)) - } - if extendedConstraint != nil && extendedConstraint.flags&TypeFlagsIndex != 0 { - m.modifiersType = c.instantiateType(extendedConstraint.AsIndexType().target, m.mapper) - } else { - m.modifiersType = c.unknownType - } + return c.instantiateType(c.getTypeFromTypeNode(c.getConstraintDeclarationForMappedType(t).Type()), m.mapper) } - } - return m.modifiersType + // Otherwise, get the declared constraint type, and if the constraint type is a type parameter, + // get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T', + // the modifiers type is T. Otherwise, the modifiers type is unknown. + declaredType := memoTypeNode(c, m.declaration.AsNode(), (*Checker).getTypeFromMappedTypeNode) + constraint := c.getConstraintTypeFromMappedType(declaredType) + extendedConstraint := constraint + if constraint != nil && constraint.flags&TypeFlagsTypeParameter != 0 { + extendedConstraint = c.getConstraintOfTypeParameter(getNonDistributedTypeParameter(constraint)) + } + if extendedConstraint != nil && extendedConstraint.flags&TypeFlagsIndex != 0 { + return c.instantiateType(extendedConstraint.AsIndexType().target, m.mapper) + } + return c.unknownType + }) } func (c *Checker) extractTypesOfKind(t *Type, kind TypeFlags) *Type { @@ -29398,9 +31317,10 @@ func (c *Checker) getRegularTypeOfObjectLiteral(t *Type) *Type { return t } key := CachedTypeKey{kind: CachedTypeKindRegularObjectLiteral, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { + if cached, _ := c.cachedTypes.get(c, key); cached != nil { return cached } + c.cachedTypes.begin(c, key) resolved := c.resolveStructuredTypeMembers(t) members := c.transformTypeOfMembers(t, c.getRegularTypeOfObjectLiteral) create := func() *Type { @@ -29415,7 +31335,8 @@ func (c *Checker) getRegularTypeOfObjectLiteral(t *Type) *Type { } else { regular = create() } - c.cachedTypes[key] = regular + c.cachedTypes.set(c, key, regular) + c.endMemo() return regular } @@ -29822,6 +31743,45 @@ func (c *Checker) checkExternalEmitHelpers(location *ast.Node, helpers ExternalE if !ast.IsEffectiveExternalModule(sourceFile, c.compilerOptions) || location.Flags&ast.NodeFlagsAmbient != 0 { return } + if !c.checking { + c.recordEffect(func(c *Checker) { c.checkExternalEmitHelpers(location, helpers) }) + return + } + links := c.sourceFileLinks.Get(sourceFile) + if links.typeChecked { + c.checkExternalEmitHelpersAt(sourceFile, location, helpers) + return + } + links.externalHelperRequests = append(links.externalHelperRequests, externalHelperRequest{location, helpers}) +} + +type externalHelperRequest struct { + location *ast.Node + helpers ExternalEmitHelpers +} + +// checkExternalHelperRequests checks the helpers requested by checking +// sourceFile. Errors are reported once per file, at the first location +// requesting a helper in the file, rather than the first location checked, +// since the order in which checking requests helpers depends on what was +// computed before. +func (c *Checker) checkExternalHelperRequests(sourceFile *ast.SourceFile) { + links := c.sourceFileLinks.Get(sourceFile) + requests := links.externalHelperRequests + links.externalHelperRequests = nil + slices.SortStableFunc(requests, func(a, b externalHelperRequest) int { + return cmp.Or( + cmp.Compare(a.location.Pos(), b.location.Pos()), + cmp.Compare(b.location.End(), a.location.End()), + cmp.Compare(a.location.Kind, b.location.Kind), + ) + }) + for _, r := range requests { + c.checkExternalEmitHelpersAt(sourceFile, r.location, r.helpers) + } +} + +func (c *Checker) checkExternalEmitHelpersAt(sourceFile *ast.SourceFile, location *ast.Node, helpers ExternalEmitHelpers) { helpersModule := c.resolveHelpersModule(sourceFile, location) if helpersModule == c.unknownSymbol { return @@ -30068,20 +32028,30 @@ func (c *Checker) markAliasReferenced(symbol *ast.Symbol, location *ast.Node) { // the alias as an expression (which recursively takes us back here if the target references another alias). func (c *Checker) markAliasSymbolAsReferenced(symbol *ast.Symbol) { links := c.aliasSymbolLinks.Get(symbol) - if !links.referenced { - links.referenced = true - node := c.getDeclarationOfAliasSymbol(symbol) - if node == nil { - panic("Unexpected nil in markAliasSymbolAsReferenced") - } - // We defer checking of the reference of an `import =` until the import itself is referenced, - // This way a chain of imports can be elided if ultimately the final input is only used in a type - // position. - if ast.IsImportEqualsDeclaration(node) && node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference { - if c.getSymbolFlags(c.resolveSymbol(symbol))&ast.SymbolFlagsValue != 0 { - // import foo = - left := ast.GetFirstIdentifier(node.AsImportEqualsDeclaration().ModuleReference) - c.markIdentifierAliasReferenced(left) + if links.referenced { + return + } + links.referenced = true + c.followAliasReference(symbol) +} + +// followAliasReference marks referenced what the referenced alias symbol +// refers to. We defer checking of the reference of an `import =` until the +// import itself is referenced, this way a chain of imports can be elided if +// ultimately the final input is only used in a type position. Following it +// reports nothing, so that whatever marks the alias first can follow it; +// checking the declaration reports what is wrong with its reference. +func (c *Checker) followAliasReference(symbol *ast.Symbol) { + node := c.getDeclarationOfAliasSymbol(symbol) + if node == nil { + panic("Unexpected nil in markAliasSymbolAsReferenced") + } + if ast.IsImportEqualsDeclaration(node) && node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference { + if c.getSymbolFlags(c.resolveSymbol(symbol))&ast.SymbolFlagsValue != 0 { + // import foo = + left := ast.GetFirstIdentifier(node.AsImportEqualsDeclaration().ModuleReference) + if target := c.resolveName(left, left.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/); target != nil { + c.markAliasReferenced(target, left) } } } @@ -30180,13 +32150,29 @@ func (c *Checker) getPromisedTypeOfPromiseEx(t *Type, errorNode *ast.Node, thisT return nil } key := CachedTypeKey{kind: CachedTypeKindPromisedTypeOfPromise, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { + if cached, _ := c.cachedTypes.get(c, key); cached != nil { return cached } + if errorNode != nil { + // Errors belong to errorNode; see getIterationTypesOfIterable. Only + // a missing result reports any, so a result is shared all the same. + if result := c.getPromisedTypeOfPromiseEx(t, nil /*errorNode*/, thisTypeForErrorOut); result != nil { + return result + } + return c.getPromisedTypeOfPromiseWorker(t, errorNode, thisTypeForErrorOut) + } + c.cachedTypes.begin(c, key) + defer c.endMemo() + result := c.getPromisedTypeOfPromiseWorker(t, nil /*errorNode*/, thisTypeForErrorOut) + if result != nil { + c.cachedTypes.set(c, key, result) + } + return result +} + +func (c *Checker) getPromisedTypeOfPromiseWorker(t *Type, errorNode *ast.Node, thisTypeForErrorOut **Type) *Type { if c.isReferenceToType(t, c.getGlobalPromiseType()) { - result := c.getTypeArguments(t)[0] - c.cachedTypes[key] = result - return result + return c.getTypeArguments(t)[0] } // primitives with a `{ then() }` won't be unwrapped/adopted. if c.allTypesAssignableToKind(c.getBaseConstraintOrType(t), TypeFlagsPrimitive|TypeFlagsNever) { @@ -30238,9 +32224,7 @@ func (c *Checker) getPromisedTypeOfPromiseEx(t *Type, errorNode *ast.Node, thisT } return nil } - result := c.getUnionTypeEx(core.Map(onfulfilledParameterSignatures, c.getTypeOfFirstParameterOfSignature), UnionReductionSubtype, nil, nil) - c.cachedTypes[key] = result - return result + return c.getUnionTypeEx(core.Map(onfulfilledParameterSignatures, c.getTypeOfFirstParameterOfSignature), UnionReductionSubtype, nil, nil) } func (c *Checker) getTypeOfFirstParameterOfSignature(signature *Signature) *Type { @@ -30447,7 +32431,7 @@ func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { } if !addSpans(texts, types) { if tooLarge { - c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) + c.errorAtCurrentNode(diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) return c.errorType } return c.stringType @@ -30466,10 +32450,10 @@ func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { } } key := getTemplateTypeKey(newTexts, newTypes) - t := c.templateLiteralTypes[key] + t, _ := c.templateLiteralTypes.get(key) if t == nil { t = c.newTemplateLiteralType(newTexts, newTypes) - c.templateLiteralTypes[key] = t + c.templateLiteralTypes.intern(key, t) } return t } @@ -30539,10 +32523,10 @@ func (c *Checker) applyTemplateStringMapping(symbol *ast.Symbol, texts []string, func (c *Checker) getStringMappingTypeForGenericType(symbol *ast.Symbol, t *Type) *Type { key := StringMappingKey{s: symbol, t: t} - result := c.stringMappingTypes[key] + result, _ := c.stringMappingTypes.get(key) if result == nil { result = c.newStringMappingType(symbol, t) - c.stringMappingTypes[key] = result + c.stringMappingTypes.intern(key, result) } return result } @@ -30732,8 +32716,7 @@ func (c *Checker) getContextuallyTypedParameterType(parameter *ast.Node) *Type { return c.getSpreadArgumentType(args, indexOfParameter, len(args), c.anyType, nil /*context*/, CheckModeNormal) } links := c.signatureLinks.Get(iife) - cached := links.resolvedSignature - links.resolvedSignature = c.anySignature + cached := links.resolvedSignature.swap(c.anySignature) var t *Type switch { case indexOfParameter < len(args): @@ -30743,7 +32726,7 @@ func (c *Checker) getContextuallyTypedParameterType(parameter *ast.Node) *Type { default: t = c.undefinedWideningType } - links.resolvedSignature = cached + links.resolvedSignature.swap(cached) return t } contextualSignature := c.getContextualSignature(fn) @@ -30937,6 +32920,13 @@ func (c *Checker) getContextualReturnType(functionDecl *ast.Node, contextFlags C // Otherwise, if the containing function is contextually typed by a function type with exactly one call signature // and that call signature is non-generic, return statements are contextually typed by the return type of the signature signature := c.getContextualSignatureForFunctionLikeDeclaration(functionDecl) + // A function whose own signature is its contextual signature cannot + // contextually type its returns while their type is being inferred, even + // when that started before the resolution of an enclosing call, which + // hides it from isResolvingReturnTypeOfSignature. + if signature != nil && signature.declaration == functionDecl && c.resolvingAnywhereIndex(signature, TypeSystemPropertyNameResolvedReturnType) >= 0 { + signature = nil + } if signature != nil && !c.isResolvingReturnTypeOfSignature(signature) { returnType := c.getReturnTypeOfSignature(signature) functionFlags := ast.GetFunctionFlags(functionDecl) @@ -31048,8 +33038,14 @@ func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argI // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. var signature *Signature - if c.signatureLinks.Get(callTarget).resolvedSignature == c.resolvingSignature { + if links := c.signatureLinks.Get(callTarget); links.resolvedSignature.get(c) == c.resolvingSignature && !c.isSignatureResolutionHidden(links) { signature = c.resolvingSignature + // The marker is the incomplete result of the call, so what is + // derived from it, such as implicit any errors for the parameters of + // a callback, is dropped if it is discarded. + if i := c.resolvingAnywhereIndex(callTarget, TypeSystemPropertyNameCallResolution); i >= 0 { + c.markReadIncomplete(i) + } } else { signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal) } @@ -31093,7 +33089,7 @@ func (c *Checker) getContextualTypeForBinaryOperand(node *ast.Node, contextFlags // by the type of the left operand, except for the special case of Javascript declarations of the form // `namespace.prop = namespace.prop || {}`. t := c.getContextualType(binary.AsNode(), contextFlags) - if node == binary.Right && (t == nil || c.patternForType[t] != nil) { + if node == binary.Right && (t == nil || c.patternForType.has(t)) { return c.getTypeOfExpression(binary.Left) } return t @@ -31427,15 +33423,16 @@ func (c *Checker) getDecoratorCallSignature(decorator *ast.Node) *Signature { func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signature { node := decorator.Parent links := c.signatureLinks.Get(node) - if links.decoratorSignature == nil { - links.decoratorSignature = c.anySignature + if links.decoratorSignature.get(c) == nil { + links.decoratorSignature.begin(c) + links.decoratorSignature.set(c, c.anySignature) switch node.Kind { case ast.KindClassDeclaration, ast.KindClassExpression: // For a class decorator, the `target` is the type of the class (e.g. the // "static" or "constructor" side of the class). targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) targetParam := c.newParameter("target", targetType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam}, c.getUnionType([]*Type{targetType, c.voidType})) + links.decoratorSignature.set(c, c.newCallSignature(nil, nil, []*ast.Symbol{targetParam}, c.getUnionType([]*Type{targetType, c.voidType}))) case ast.KindParameter: if !ast.IsConstructorDeclaration(node.Parent) && !(ast.IsMethodDeclaration(node.Parent) || ast.IsSetAccessorDeclaration(node.Parent) && ast.IsClassLike(node.Parent.Parent)) { break @@ -31460,7 +33457,7 @@ func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signatur targetParam := c.newParameter("target", targetType) keyParam := c.newParameter("propertyKey", keyType) indexParam := c.newParameter("parameterIndex", indexType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, indexParam}, c.voidType) + links.decoratorSignature.set(c, c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, indexParam}, c.voidType)) case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration: if !ast.IsClassLike(node.Parent) { break @@ -31479,16 +33476,17 @@ func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signatur if hasPropDesc { descriptorType := c.newTypedPropertyDescriptorType(c.getTypeOfNode(node)) descriptorParam := c.newParameter("descriptor", descriptorType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, descriptorParam}, c.getUnionType([]*Type{returnType, c.voidType})) + links.decoratorSignature.set(c, c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, descriptorParam}, c.getUnionType([]*Type{returnType, c.voidType}))) } else { - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam}, c.getUnionType([]*Type{returnType, c.voidType})) + links.decoratorSignature.set(c, c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam}, c.getUnionType([]*Type{returnType, c.voidType}))) } } + c.endMemo() } - if links.decoratorSignature == c.anySignature { + if links.decoratorSignature.get(c) == c.anySignature { return nil } - return links.decoratorSignature + return links.decoratorSignature.get(c) } func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { @@ -31574,15 +33572,16 @@ func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { // `undefined` then the "output type" is the "input type"). node := decorator.Parent links := c.signatureLinks.Get(node) - if links.decoratorSignature == nil { - links.decoratorSignature = c.anySignature + if links.decoratorSignature.get(c) == nil { + links.decoratorSignature.begin(c) + links.decoratorSignature.set(c, c.anySignature) switch node.Kind { case ast.KindClassDeclaration, ast.KindClassExpression: // Class decorators have a `context` of `ClassDecoratorContext`, where the `Class` type // argument will be the "final type" of the class after all decorators are applied. targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) contextType := c.newClassDecoratorContextType(targetType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) + links.decoratorSignature.set(c, c.newESDecoratorCallSignature(targetType, contextType, targetType)) case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: if !ast.IsClassLike(node.Parent) { break @@ -31623,7 +33622,7 @@ func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { targetType = valueType } contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) + links.decoratorSignature.set(c, c.newESDecoratorCallSignature(targetType, contextType, targetType)) case ast.KindPropertyDeclaration: if !ast.IsClassLike(node.Parent) { break @@ -31660,13 +33659,14 @@ func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { returnType = c.newClassFieldDecoratorInitializerMutatorType(thisType, valueType) } contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, returnType) + links.decoratorSignature.set(c, c.newESDecoratorCallSignature(targetType, contextType, returnType)) } + c.endMemo() } - if links.decoratorSignature == c.anySignature { + if links.decoratorSignature.get(c) == c.anySignature { return nil } - return links.decoratorSignature + return links.decoratorSignature.get(c) } func (c *Checker) newClassDecoratorContextType(classType *Type) *Type { @@ -31702,7 +33702,7 @@ func (c *Checker) getClassMemberDecoratorContextOverrideType(nameType *Type, isP core.IfElse(isStatic, CachedTypeKindDecoratorContextStatic, CachedTypeKindDecoratorContext), ) key := CachedTypeKey{kind: kind, typeId: nameType.id} - if overrideType := c.cachedTypes[key]; overrideType != nil { + if overrideType, _ := c.cachedTypes.get(c, key); overrideType != nil { return overrideType } members := ast.NewSymbolTable() @@ -31710,7 +33710,7 @@ func (c *Checker) getClassMemberDecoratorContextOverrideType(nameType *Type, isP members.Set("private", c.newProperty("private", core.IfElse(isPrivate, c.trueType, c.falseType))) members.Set("static", c.newProperty("static", core.IfElse(isStatic, c.trueType, c.falseType))) overrideType := c.newAnonymousType(nil, members, nil, nil, nil) - c.cachedTypes[key] = overrideType + c.cachedTypes.intern(key, overrideType) return overrideType } @@ -31876,7 +33876,7 @@ func (c *Checker) getIndexedMappedTypeSubstitutedTypeOfContextualType(t *Type, n } constraint := c.getConstraintTypeFromMappedType(t) // special case for conditional types pretending to be negated types - if t.AsMappedType().nameType != nil && c.isExcludedMappedPropertyName(t.AsMappedType().nameType, propertyNameType) || c.isExcludedMappedPropertyName(constraint, propertyNameType) { + if mappedNameType := c.getNameTypeFromMappedType(t); mappedNameType != nil && c.isExcludedMappedPropertyName(mappedNameType, propertyNameType) || c.isExcludedMappedPropertyName(constraint, propertyNameType) { return nil } constraintOfConstraint := c.getBaseConstraintOrType(constraint) @@ -31928,7 +33928,7 @@ func (c *Checker) getTypeFromIndexInfosOfContextualType(t *Type, name string, na func (c *Checker) isCircularMappedProperty(symbol *ast.Symbol) bool { if symbol.CheckFlags()&ast.CheckFlagsMapped != 0 { links := c.valueSymbolLinks.Get(symbol) - return links.resolvedType == nil && c.findResolutionCycleStartIndex(symbol, TypeSystemPropertyNameType) >= 0 + return links.resolvedType.get(c) == nil && c.isResolutionInProgress(symbol, TypeSystemPropertyNameType) } return false } @@ -32019,9 +34019,10 @@ func (d *ObjectLiteralDiscriminator) matches(index int, t *Type) bool { func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, contextualType *Type) *Type { key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id} - if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil { + if discriminated, _ := c.discriminatedContextualTypes.get(c, key); discriminated != nil { return discriminated } + c.discriminatedContextualTypes.begin(c, key) discriminated := c.getMatchingUnionConstituentForObjectLiteral(contextualType, node) if discriminated == nil { discriminantProperties := core.Filter(node.Properties(), func(p *ast.Node) bool { @@ -32043,7 +34044,8 @@ func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, cont discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} discriminated = c.discriminateTypeByDiscriminableItems(contextualType, discriminator) } - c.discriminatedContextualTypes[key] = discriminated + c.discriminatedContextualTypes.set(c, key, discriminated) + c.endMemo() return discriminated } @@ -32409,7 +34411,7 @@ func (c *Checker) isFunctionObjectType(t *Type) bool { // We do a quick check for a "bind" property before performing the more expensive subtype // check. This gives us a quicker out in the common case where an object type is not a function. resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.getSignatures()) != 0 || resolved.getMember("bind") != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) + return len(resolved.getSignatures()) != 0 || resolved.getMember(c, "bind") != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) } func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type { @@ -32542,32 +34544,53 @@ func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnost } // If we've already cached an awaited type, return a possible `Awaited` for it. key := CachedTypeKey{kind: CachedTypeKindAwaitedType, typeId: t.id} - if awaitedType := c.cachedTypes[key]; awaitedType != nil { + if awaitedType, _ := c.cachedTypes.get(c, key); awaitedType != nil { return awaitedType } + if errorNode != nil { + // Errors belong to errorNode; see getIterationTypesOfIterable. Only a + // result computing which reports nothing is cached, so it is shared + // all the same. + if c.getAwaitedTypeNoAliasEx(t, nil /*errorNode*/, nil /*diagnosticMessage*/) != nil { + if awaitedType, _ := c.cachedTypes.get(c, key); awaitedType != nil { + return awaitedType + } + } + return c.getAwaitedTypeNoAliasWorker(t, errorNode, diagnosticMessage, args...) + } + c.cachedTypes.begin(c, key) + defer c.endMemo() + errors := c.awaitedTypeErrors + awaitedType := c.getAwaitedTypeNoAliasWorker(t, nil /*errorNode*/, nil /*diagnosticMessage*/) + if awaitedType != nil && c.awaitedTypeErrors == errors { + c.cachedTypes.set(c, key, awaitedType) + } + return awaitedType +} + +func (c *Checker) getAwaitedTypeNoAliasWorker(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type { // For a union, get a union of the awaited types of each constituent. if t.flags&TypeFlagsUnion != 0 { - if slices.Contains(c.awaitedTypeStack, t) { + if c.isAwaitingType(t) { + c.awaitedTypeErrors++ if errorNode != nil { c.error(errorNode, diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method) } return nil } - c.awaitedTypeStack = append(c.awaitedTypeStack, t) + c.pushAwaitingType(t) mapped := c.mapType(t, func(t *Type) *Type { return c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage, args...) }) - c.awaitedTypeStack = c.awaitedTypeStack[:len(c.awaitedTypeStack)-1] - c.cachedTypes[key] = mapped + c.popTypeResolution() return mapped } // If `type` is generic and should be wrapped in `Awaited`, return it. if c.isAwaitedTypeNeeded(t) { - c.cachedTypes[key] = t return t } var thisTypeForError *Type promisedType := c.getPromisedTypeOfPromiseEx(t, nil /*errorNode*/, &thisTypeForError) if promisedType != nil { - if t == promisedType || slices.Contains(c.awaitedTypeStack, promisedType) { + if t == promisedType || c.isAwaitingType(promisedType) { // Verify that we don't have a bad actor in the form of a promise whose // promised type is the same as the promise type, or a mutually recursive // promise. If so, we return undefined as we cannot guess the shape. If this @@ -32601,6 +34624,7 @@ func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnost // onrejected: (error: any) => any): BadPromiseA; // } // + c.awaitedTypeErrors++ if errorNode != nil { c.error(errorNode, diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method) } @@ -32608,13 +34632,9 @@ func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnost } // Keep track of the type we're about to unwrap to avoid bad recursive promise types. // See the comments above for more information. - c.awaitedTypeStack = append(c.awaitedTypeStack, t) + c.pushAwaitingType(t) awaitedType := c.getAwaitedTypeNoAliasEx(promisedType, errorNode, diagnosticMessage, args...) - c.awaitedTypeStack = c.awaitedTypeStack[:len(c.awaitedTypeStack)-1] - if awaitedType == nil { - return nil - } - c.cachedTypes[key] = awaitedType + c.popTypeResolution() return awaitedType } // The type was not a promise, so it could not be unwrapped any further. @@ -32633,6 +34653,7 @@ func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnost // function, they would need to wrap it in some other value. If they want it to // be treated as a promise, they can cast to . if c.isThenableType(t) { + c.awaitedTypeErrors++ if errorNode != nil { var diagnostic *ast.Diagnostic if thisTypeForError != nil { @@ -32642,10 +34663,21 @@ func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnost } return nil } - c.cachedTypes[key] = t return t } +// pushAwaitingType records that the awaited type of t is being computed, until +// the matching popTypeResolution. +func (c *Checker) pushAwaitingType(t *Type) { + c.beginTypeResolution(t, TypeSystemPropertyNameAwaitedType, nil) +} + +// isAwaitingType reports whether the awaited type of t is being computed; +// see isResolvingAnywhere. +func (c *Checker) isAwaitingType(t *Type) bool { + return c.isResolvingAnywhere(t, TypeSystemPropertyNameAwaitedType) +} + func (c *Checker) isAwaitedTypeInstantiation(t *Type) bool { if t.flags&TypeFlagsConditional != 0 { awaitedSymbol := c.getGlobalAwaitedSymbolOrNil() @@ -32905,7 +34937,7 @@ func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Sy // member should more exactly be the kind of (declarationless) symbol we want. // (See #44364 and #45031 for relevant implementation PRs) if metaProp.KeywordToken == ast.KindImportKeyword && node.Text() == "meta" { - return c.getGlobalImportMetaExpressionType().AsObjectType().getMember("meta") + return c.getGlobalImportMetaExpressionType().AsObjectType().getMember(c, "meta") } // no other meta properties are valid syntax, thus no others should have symbols return nil @@ -32939,7 +34971,7 @@ func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Sy } fallthrough case ast.KindThisType: - return c.getTypeFromThisTypeNode(node).symbol + return memoTypeNode(c, node, (*Checker).getTypeFromThisTypeNode).symbol case ast.KindSuperKeyword: return c.checkExpression(node).symbol case ast.KindConstructorKeyword: @@ -33138,12 +35170,16 @@ func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast return c.getSymbolForPrivateIdentifierExpression(name) } else if ast.IsPropertyAccessExpression(name) || ast.IsQualifiedName(name) { links := c.symbolNodeLinks.Get(name) - if links.resolvedSymbol != nil { - return links.resolvedSymbol + if links.resolvedSymbol.get(c) != nil { + return links.resolvedSymbol.get(c) } + // The symbol is read back from links after checking, before a query + // finishing would discard it. + query, ok := c.enterQuery() + defer c.exitQuery(query, ok) if ast.IsPropertyAccessExpression(name) { c.checkPropertyAccessExpression(name, CheckModeNormal, false /*writeOnly*/) - if links.resolvedSymbol == nil && !ast.IsPrivateIdentifier(name.Name()) { + if links.resolvedSymbol.get(c) == nil && !ast.IsPrivateIdentifier(name.Name()) { // Not cached in links, where checking would observe it as the // property the access resolved to. return c.getApplicableIndexSymbol( @@ -33154,10 +35190,10 @@ func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast } else { c.checkQualifiedName(name, CheckModeNormal) } - if links.resolvedSymbol == nil && isJSDoc && ast.IsQualifiedName(name) { + if links.resolvedSymbol.get(c) == nil && isJSDoc && ast.IsQualifiedName(name) { return c.resolveJSDocMemberName(name) } - return links.resolvedSymbol + return links.resolvedSymbol.get(c) } } else if ast.IsEntityName(name) && isTypeReferenceIdentifier(name) { meaning := core.IfElse(name.Parent.Kind == ast.KindTypeReference, ast.SymbolFlagsType, ast.SymbolFlagsNamespace) @@ -33374,7 +35410,7 @@ func (c *Checker) getApplicableIndexSymbol(t *Type, keyType *Type) *ast.Symbol { symbol.SetValueDeclaration(declarations[0]) symbol.SetParent(t.symbol) links := c.valueSymbolLinks.Get(symbol) - links.resolvedType = info.valueType + links.resolvedType.setIdentity(c, info.valueType) info.indexSymbol = symbol } } @@ -33395,9 +35431,10 @@ func (c *Checker) containsArgumentsReference(node *ast.Node) bool { return false } - if containsArguments, ok := c.cachedArgumentsReferenced[node]; ok { + if containsArguments, ok := c.cachedArgumentsReferenced.get(c, node); ok { return containsArguments } + c.cachedArgumentsReferenced.begin(c, node) var visit func(node *ast.Node) bool visit = func(node *ast.Node) bool { @@ -33423,7 +35460,8 @@ func (c *Checker) containsArgumentsReference(node *ast.Node) bool { } containsArguments := visit(node.Body()) - c.cachedArgumentsReferenced[node] = containsArguments + c.cachedArgumentsReferenced.set(c, node, containsArguments) + c.endMemo() return containsArguments } diff --git a/tsc/internal/checker/checker_test.go b/tsc/internal/checker/checker_test.go index 42ae368d35924..34666ff86c4c5 100644 --- a/tsc/internal/checker/checker_test.go +++ b/tsc/internal/checker/checker_test.go @@ -2,7 +2,11 @@ package checker_test import ( "fmt" + goast "go/ast" + "go/parser" + "go/token" "path/filepath" + "slices" "strings" "testing" @@ -177,6 +181,46 @@ func BenchmarkCheckFreshProgram(b *testing.B) { } } +func TestQueriesDoNotMoveErrorsAtCurrentNode(t *testing.T) { + t.Parallel() + + // Asking for the symbol of t.x computes T<"x"> at no node, reporting + // an excessive depth there. Asking for the type of tx then reuses it at + // t.x, so checking reports the error at t.x and not at tx. + content := `type T = { + x: T[K]; + y: number; +}; + +declare let t: T<"x">; +let tx = t.x; +` + diagnostics := func(query bool) []string { + fs := bundled.WrapFS(vfstest.FromMap(map[string]string{ + "/foo.ts": content, + "/tsconfig.json": `{"files": ["foo.ts"]}`, + }, tspath.CaseInsensitive)) + host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil) + parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, nil, tsoptions.NewDefaultParseConfigHost(fs), nil) + assert.Equal(t, len(errors), 0) + p := compiler.NewProgram(compiler.ProgramOptions{Config: parsed, Host: host}) + file := p.GetSourceFile("/foo.ts") + if query { + c, done := p.GetTypeCheckerForFile(t.Context(), file) + decl := file.Statements.Nodes[2].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0] + c.GetSymbolAtLocation(decl.Initializer().Name()) + c.GetTypeAtLocation(decl.Name()) + done() + } + var result []string + for _, d := range p.GetSemanticDiagnostics(t.Context(), file) { + result = append(result, fmt.Sprintf("%d: TS%d", d.Pos(), d.Code())) + } + return result + } + assert.DeepEqual(t, diagnostics(true), diagnostics(false)) +} + func TestTypeAllocationAcrossCheckerGrowth(t *testing.T) { t.Parallel() var content strings.Builder @@ -208,3 +252,467 @@ export type Initial = Box<"first">; assert.Equal(t, c.TypeToString(initial), "Initial") assert.Equal(t, len(c.GetDiagnostics(t.Context(), file)), 0) } + +// Memoized results must only be accessed through memo methods, which account +// for the effects of computing them. +func TestMemoValueOnlyAccessedThroughMethods(t *testing.T) { + t.Parallel() + fset := token.NewFileSet() + files, err := filepath.Glob("*.go") + assert.NilError(t, err) + for _, name := range files { + if name == "memo.go" || strings.HasSuffix(name, "_test.go") { + continue + } + file, err := parser.ParseFile(fset, name, nil, parser.SkipObjectResolution) + assert.NilError(t, err) + goast.Inspect(file, func(n goast.Node) bool { + if sel, ok := n.(*goast.SelectorExpr); ok && sel.Sel.Name == "memoValue" { + t.Errorf("%s: memo value accessed directly; use get or set", fset.Position(sel.Pos())) + } + return true + }) + } +} + +// Mutable cached state must live in memos, which account for the effects of +// computing it, or in internMaps, whose entries have none. Other fields must +// be listed in unmemoizedCacheFields with the reason they need not be. +func TestCachedStateIsMemoized(t *testing.T) { + t.Parallel() + fset := token.NewFileSet() + files, err := filepath.Glob("*.go") + assert.NilError(t, err) + seen := make(map[string]bool) + var parsed []*goast.File + // Entries interned by pointer own the results cached in them. + interned := make(map[string]bool) + for _, name := range files { + if strings.HasSuffix(name, "_test.go") { + continue + } + file, err := parser.ParseFile(fset, name, nil, parser.SkipObjectResolution) + assert.NilError(t, err) + parsed = append(parsed, file) + goast.Inspect(file, func(n goast.Node) bool { + if e, ok := n.(*goast.IndexListExpr); ok { + if ident, ok := e.X.(*goast.Ident); ok && ident.Name == "internMap" && len(e.Indices) == 2 { + if star, ok := e.Indices[1].(*goast.StarExpr); ok { + if elem, ok := star.X.(*goast.Ident); ok { + interned[elem.Name] = true + } + } + } + } + return true + }) + } + for _, file := range parsed { + name := filepath.Base(fset.File(file.Pos()).Name()) + goast.Inspect(file, func(n goast.Node) bool { + spec, ok := n.(*goast.TypeSpec) + if !ok { + return true + } + st, ok := spec.Type.(*goast.StructType) + if !ok || slices.Contains(notCacheOwners, spec.Name.Name) || name != "types.go" && !strings.HasSuffix(spec.Name.Name, "Links") && spec.Name.Name != "memoStore" && !interned[spec.Name.Name] { + return true + } + for _, field := range st.Fields.List { + if isMemoType(field.Type) { + continue + } + for _, fieldName := range field.Names { + key := spec.Name.Name + "." + fieldName.Name + seen[key] = true + if _, ok := unmemoizedCacheFields[key]; !ok { + t.Errorf("%s: %s is not a memo; make it one or list it in unmemoizedCacheFields", fset.Position(fieldName.Pos()), key) + } + } + } + return false + }) + } + for key := range unmemoizedCacheFields { + if !seen[key] { + t.Errorf("unmemoizedCacheFields lists %s, which is not an unmemoized field", key) + } + } +} + +func isMemoType(expr goast.Expr) bool { + if a, ok := expr.(*goast.ArrayType); ok { + expr = a.Elt + } + switch e := expr.(type) { + case *goast.IndexExpr: + expr = e.X + case *goast.IndexListExpr: + expr = e.X + } + ident, ok := expr.(*goast.Ident) + return ok && (strings.HasPrefix(ident.Name, "memo") || ident.Name == "internMap") +} + +const ( + unmemoizedImmutable = "Set when the store is created" + unmemoizedContainer = "Holds links, whose fields are checked separately" + unmemoizedCounter = "Allocation counter for IDs and statistics" + unmemoizedInterned = "Interned type whose creation has no effects" + unmemoizedProgram = "Derived from the program only; computing it has no effects" + unmemoizedMemoState = "State of the memos themselves" + unmemoizedChecking = "Written only by checks" + unmemoizedCreated = "Set when the entity owning the links is created" + unmemoizedSyntax = "Derived from syntax and options only; computing it has no effects" + unmemoizedGuarded = "Written only within the memoized computation it is part of" + unmemoizedEmit = "Emit resolver state, written only while resolving for emit" + unmemoizedMark = "Written only by checks and emit marking, and read only by emit" + unmemoizedNodeBuilder = "Node builder state, scoped to one node builder" + unmemoizedNodeFlags = "Each flag is memo-guarded, reporting-only, or set whenever a node is checked and read only by deferred diagnostics, after the whole file is checked" + unmemoizedFlagMemo = "Flag memo, accounted for like a memo under a key naming its entity and field" + unmemoizedEntityFlags = "Each flag is set when the entity is created, or is part of a flag memo" + unmemoizedDerived = "Derived only from fields set when the entity is created; computing it has no effects" + unmemoizedLoopFixpoint = "Fixed point of a loop, kept once complete; analyzing it again from another reference may differ" +) + +// notCacheOwners are the structs declared in types.go that are plain values +// rather than entities owning cached state. +var notCacheOwners = []string{"LanguageFeatureMinimumTargetMap", "accessibleChainCacheKey", "computedName", "TupleElementInfo", "MemberInfo"} + +var unmemoizedCacheFields = map[string]string{ + "valueSymbolExtra.nameType": unmemoizedCreated, + "valueSymbolExtra.containingType": unmemoizedCreated, + "TypeAlias.symbol": unmemoizedCreated, + "TypeAlias.typeArguments": unmemoizedCreated, + "Type.flags": unmemoizedCreated, + "Type.objectFlags": unmemoizedEntityFlags, + "Type.id": unmemoizedCounter, + "Type.kind": unmemoizedCreated, + "Type.generation": unmemoizedDerived, + "Type.symbol": unmemoizedCreated, + "Type.alias": unmemoizedCreated, + "Type.checker": unmemoizedImmutable, + "IntrinsicType.intrinsicName": unmemoizedCreated, + "LiteralType.value": unmemoizedCreated, + "LiteralType.freshType": unmemoizedInterned, + "LiteralType.regularType": unmemoizedCreated, + "UniqueESSymbolType.name": unmemoizedCreated, + "StructuredType.members": unmemoizedFlagMemo, + "StructuredType.properties": unmemoizedFlagMemo, + "StructuredType.extra": unmemoizedFlagMemo, + "structuredTypeExtra.signatures": unmemoizedFlagMemo, + "structuredTypeExtra.callSignatureCount": unmemoizedFlagMemo, + "structuredTypeExtra.indexInfos": unmemoizedFlagMemo, + "structuredTypeExtra.objectTypeWithoutAbstractConstructSignatures": unmemoizedInterned, + "ObjectType.target": unmemoizedCreated, + "ObjectType.mapper": unmemoizedCreated, + "TypeReference.node": unmemoizedCreated, + "TypeReference.lazy": unmemoizedFlagMemo, + "LazyState.layout": unmemoizedCreated, + "LazyState.mapper": unmemoizedCreated, + "LazyState.baseTypes": unmemoizedCreated, + "LazyState.propertiesResolved": unmemoizedFlagMemo, + "MemberLayout.id": unmemoizedCounter, + "MemberLayout.baseIndices": unmemoizedCreated, + "MemberLayout.members": unmemoizedCreated, + "MemberLayout.layoutProperties": unmemoizedDerived, + "InterfaceType.allTypeParameters": unmemoizedCreated, + "InterfaceType.outerTypeParameterCount": unmemoizedCreated, + "InterfaceType.thisType": unmemoizedCreated, + "InterfaceType.baseTypesResolved": unmemoizedFlagMemo, + "InterfaceType.declaredMembersResolved": unmemoizedFlagMemo, + "InterfaceType.declaredMembers": unmemoizedFlagMemo, + "InterfaceType.declaredCallSignatures": unmemoizedFlagMemo, + "InterfaceType.declaredConstructSignatures": unmemoizedFlagMemo, + "InterfaceType.declaredIndexInfos": unmemoizedFlagMemo, + "TupleType.elementInfos": unmemoizedCreated, + "TupleType.minLength": unmemoizedCreated, + "TupleType.fixedLength": unmemoizedCreated, + "TupleType.combinedFlags": unmemoizedCreated, + "TupleType.readonly": unmemoizedCreated, + "InstantiationExpressionType.node": unmemoizedCreated, + "MappedType.declaration": unmemoizedCreated, + "ReverseMappedType.source": unmemoizedCreated, + "ReverseMappedType.mappedType": unmemoizedCreated, + "ReverseMappedType.constraintType": unmemoizedCreated, + "EvolvingArrayType.elementType": unmemoizedCreated, + "UnionOrIntersectionType.types": unmemoizedCreated, + "UnionType.regularType": unmemoizedInterned, + "UnionType.origin": unmemoizedCreated, + "UnionType.constituentMap": unmemoizedGuarded, + "TypeParameter.target": unmemoizedCreated, + "TypeParameter.mapper": unmemoizedCreated, + "TypeParameter.isThisType": unmemoizedCreated, + "TypeParameter.isDistributed": unmemoizedCreated, + "TypeParameter.distributedType": unmemoizedInterned, + "TypeParameter.origin": unmemoizedCreated, + "IndexType.target": unmemoizedCreated, + "IndexType.indexFlags": unmemoizedCreated, + "IndexedAccessType.objectType": unmemoizedCreated, + "IndexedAccessType.indexType": unmemoizedCreated, + "IndexedAccessType.accessFlags": unmemoizedCreated, + "TemplateLiteralType.texts": unmemoizedCreated, + "TemplateLiteralType.types": unmemoizedCreated, + "StringMappingType.target": unmemoizedCreated, + "SubstitutionType.baseType": unmemoizedCreated, + "SubstitutionType.constraint": unmemoizedCreated, + "ConditionalRoot.node": unmemoizedCreated, + "ConditionalRoot.checkType": unmemoizedCreated, + "ConditionalRoot.extendsType": unmemoizedCreated, + "ConditionalRoot.isDistributive": unmemoizedCreated, + "ConditionalRoot.inferTypeParameters": unmemoizedCreated, + "ConditionalRoot.outerTypeParameters": unmemoizedCreated, + "ConditionalRoot.alias": unmemoizedCreated, + "ConditionalType.root": unmemoizedCreated, + "ConditionalType.checkType": unmemoizedCreated, + "ConditionalType.extendsType": unmemoizedCreated, + "ConditionalType.mapper": unmemoizedCreated, + "ConditionalType.combinedMapper": unmemoizedCreated, + "Signature.id": unmemoizedCreated, + "Signature.flags": unmemoizedEntityFlags, + "Signature.minArgumentCount": unmemoizedCreated, + "Signature.declaration": unmemoizedCreated, + "Signature.parameters": unmemoizedCreated, + "Signature.target": unmemoizedCreated, + "Signature.mapper": unmemoizedCreated, + "Signature.extra": unmemoizedCreated, + "signatureExtra.typeParameters": unmemoizedCreated, + "signatureExtra.thisParameter": unmemoizedCreated, + "signatureExtra.resolvedTypePredicate": unmemoizedFlagMemo, + "signatureExtra.isolatedSignatureType": unmemoizedInterned, + "signatureExtra.composite": unmemoizedCreated, + "CompositeSignature.isUnion": unmemoizedCreated, + "CompositeSignature.signatures": unmemoizedCreated, + "TypePredicate.kind": unmemoizedCreated, + "TypePredicate.parameterIndex": unmemoizedCreated, + "TypePredicate.parameterName": unmemoizedCreated, + "TypePredicate.t": unmemoizedCreated, + "IndexInfo.keyType": unmemoizedCreated, + "IndexInfo.valueType": unmemoizedCreated, + "IndexInfo.isReadonly": unmemoizedCreated, + "IndexInfo.declaration": unmemoizedCreated, + "IndexInfo.indexSymbol": unmemoizedInterned, + "IndexInfo.components": unmemoizedCreated, + "memoStore.root": unmemoizedImmutable, + "memoStore.id": unmemoizedImmutable, + "memoStore.program": unmemoizedImmutable, + "memoStore.compilerOptions": unmemoizedImmutable, + "memoStore.files": unmemoizedImmutable, + "memoStore.fileIndexMap": unmemoizedImmutable, + "memoStore.languageVersion": unmemoizedImmutable, + "memoStore.moduleKind": unmemoizedImmutable, + "memoStore.moduleResolutionKind": unmemoizedImmutable, + "memoStore.legacyDecorators": unmemoizedImmutable, + "memoStore.emitStandardClassFields": unmemoizedImmutable, + "memoStore.strictNullChecks": unmemoizedImmutable, + "memoStore.strictFunctionTypes": unmemoizedImmutable, + "memoStore.strictBindCallApply": unmemoizedImmutable, + "memoStore.strictPropertyInitialization": unmemoizedImmutable, + "memoStore.strictBuiltinIteratorReturn": unmemoizedImmutable, + "memoStore.noImplicitAny": unmemoizedImmutable, + "memoStore.noImplicitThis": unmemoizedImmutable, + "memoStore.useUnknownInCatchVariables": unmemoizedImmutable, + "memoStore.exactOptionalPropertyTypes": unmemoizedImmutable, + "memoStore.canCollectSymbolAliasAccessibilityData": unmemoizedImmutable, + "memoStore.arrayVariances": unmemoizedImmutable, + "memoStore.globals": unmemoizedImmutable, + "memoStore.undefinedSymbol": unmemoizedImmutable, + "memoStore.argumentsSymbol": unmemoizedImmutable, + "memoStore.requireSymbol": unmemoizedImmutable, + "memoStore.unknownSymbol": unmemoizedImmutable, + "memoStore.globalThisSymbol": unmemoizedImmutable, + "memoStore.mergedSymbols": unmemoizedImmutable, + "memoStore.mergesFrozen": unmemoizedImmutable, + "memoStore.anyType": unmemoizedImmutable, + "memoStore.autoType": unmemoizedImmutable, + "memoStore.wildcardType": unmemoizedImmutable, + "memoStore.blockedStringType": unmemoizedImmutable, + "memoStore.errorType": unmemoizedImmutable, + "memoStore.unresolvedType": unmemoizedImmutable, + "memoStore.nonInferrableAnyType": unmemoizedImmutable, + "memoStore.intrinsicMarkerType": unmemoizedImmutable, + "memoStore.unknownType": unmemoizedImmutable, + "memoStore.undefinedType": unmemoizedImmutable, + "memoStore.undefinedWideningType": unmemoizedImmutable, + "memoStore.missingType": unmemoizedImmutable, + "memoStore.undefinedOrMissingType": unmemoizedImmutable, + "memoStore.optionalType": unmemoizedImmutable, + "memoStore.nullType": unmemoizedImmutable, + "memoStore.nullWideningType": unmemoizedImmutable, + "memoStore.stringType": unmemoizedImmutable, + "memoStore.numberType": unmemoizedImmutable, + "memoStore.bigintType": unmemoizedImmutable, + "memoStore.regularFalseType": unmemoizedImmutable, + "memoStore.falseType": unmemoizedImmutable, + "memoStore.regularTrueType": unmemoizedImmutable, + "memoStore.trueType": unmemoizedImmutable, + "memoStore.booleanType": unmemoizedImmutable, + "memoStore.esSymbolType": unmemoizedImmutable, + "memoStore.voidType": unmemoizedImmutable, + "memoStore.neverType": unmemoizedImmutable, + "memoStore.silentNeverType": unmemoizedImmutable, + "memoStore.implicitNeverType": unmemoizedImmutable, + "memoStore.unreachableNeverType": unmemoizedImmutable, + "memoStore.nonPrimitiveType": unmemoizedImmutable, + "memoStore.stringOrNumberType": unmemoizedImmutable, + "memoStore.stringNumberSymbolType": unmemoizedImmutable, + "memoStore.numberOrBigIntType": unmemoizedImmutable, + "memoStore.templateConstraintType": unmemoizedImmutable, + "memoStore.numericStringType": unmemoizedImmutable, + "memoStore.uniqueLiteralType": unmemoizedImmutable, + "memoStore.emptyObjectType": unmemoizedImmutable, + "memoStore.emptyJsxObjectType": unmemoizedImmutable, + "memoStore.emptyFreshJsxObjectType": unmemoizedImmutable, + "memoStore.emptyTypeLiteralType": unmemoizedImmutable, + "memoStore.unknownEmptyObjectType": unmemoizedImmutable, + "memoStore.unknownUnionType": unmemoizedImmutable, + "memoStore.emptyGenericType": unmemoizedImmutable, + "memoStore.anyFunctionType": unmemoizedImmutable, + "memoStore.noConstraintType": unmemoizedImmutable, + "memoStore.circularConstraintType": unmemoizedImmutable, + "memoStore.resolvingDefaultType": unmemoizedImmutable, + "memoStore.markerSuperType": unmemoizedImmutable, + "memoStore.markerSubType": unmemoizedImmutable, + "memoStore.markerOtherType": unmemoizedImmutable, + "memoStore.markerSuperTypeForCheck": unmemoizedImmutable, + "memoStore.markerSubTypeForCheck": unmemoizedImmutable, + "memoStore.noTypePredicate": unmemoizedImmutable, + "memoStore.anySignature": unmemoizedImmutable, + "memoStore.unknownSignature": unmemoizedImmutable, + "memoStore.resolvingSignature": unmemoizedImmutable, + "memoStore.globalIDs": unmemoizedImmutable, + "memoStore.silentNeverSignature": unmemoizedImmutable, + "memoStore.enumNumberIndexInfo": unmemoizedImmutable, + "memoStore.anyBaseTypeIndexInfo": unmemoizedImmutable, + "memoStore.patternAmbientModules": unmemoizedImmutable, + "memoStore.patternAmbientModuleAugmentations": unmemoizedImmutable, + "memoStore.patternAmbientModuleAugmentationTargets": unmemoizedImmutable, + "memoStore.globalObjectType": unmemoizedImmutable, + "memoStore.globalFunctionType": unmemoizedImmutable, + "memoStore.globalCallableFunctionType": unmemoizedImmutable, + "memoStore.globalNewableFunctionType": unmemoizedImmutable, + "memoStore.globalArrayType": unmemoizedImmutable, + "memoStore.globalReadonlyArrayType": unmemoizedImmutable, + "memoStore.globalStringType": unmemoizedImmutable, + "memoStore.globalNumberType": unmemoizedImmutable, + "memoStore.globalBooleanType": unmemoizedImmutable, + "memoStore.globalRegExpType": unmemoizedImmutable, + "memoStore.globalThisType": unmemoizedImmutable, + "memoStore.anyArrayType": unmemoizedImmutable, + "memoStore.autoArrayType": unmemoizedImmutable, + "memoStore.anyReadonlyArrayType": unmemoizedImmutable, + "memoStore.emptyStringType": unmemoizedImmutable, + "memoStore.zeroType": unmemoizedImmutable, + "memoStore.zeroBigIntType": unmemoizedImmutable, + "memoStore.typeofType": unmemoizedImmutable, + "memoStore.subtypeRelation": unmemoizedImmutable, + "memoStore.strictSubtypeRelation": unmemoizedImmutable, + "memoStore.assignableRelation": unmemoizedImmutable, + "memoStore.comparableRelation": unmemoizedImmutable, + "memoStore.identityRelation": unmemoizedImmutable, + "memoStore.nodeLinks": unmemoizedContainer, + "memoStore.signatureLinks": unmemoizedContainer, + "memoStore.symbolNodeLinks": unmemoizedContainer, + "memoStore.typeNodeLinks": unmemoizedContainer, + "memoStore.enumMemberLinks": unmemoizedContainer, + "memoStore.arrayLiteralLinks": unmemoizedContainer, + "memoStore.switchStatementLinks": unmemoizedContainer, + "memoStore.jsxElementLinks": unmemoizedContainer, + "memoStore.computedNameLinks": unmemoizedContainer, + "memoStore.symbolReferenceLinks": unmemoizedContainer, + "memoStore.valueSymbolLinks": unmemoizedContainer, + "memoStore.mappedSymbolLinks": unmemoizedContainer, + "memoStore.deferredSymbolLinks": unmemoizedContainer, + "memoStore.aliasSymbolLinks": unmemoizedContainer, + "memoStore.moduleSymbolLinks": unmemoizedContainer, + "memoStore.lateBoundLinks": unmemoizedContainer, + "memoStore.exportTypeLinks": unmemoizedContainer, + "memoStore.membersAndExportsLinks": unmemoizedContainer, + "memoStore.typeAliasLinks": unmemoizedContainer, + "memoStore.declaredTypeLinks": unmemoizedContainer, + "memoStore.spreadLinks": unmemoizedContainer, + "memoStore.varianceLinks": unmemoizedContainer, + "memoStore.ReverseMappedSymbolLinks": unmemoizedContainer, + "memoStore.markedAssignmentSymbolLinks": unmemoizedContainer, + "memoStore.sourceFileLinks": unmemoizedContainer, + "memoStore.emitResolverLinks": unmemoizedContainer, + "EmitResolverLinks.jsxLinks": unmemoizedContainer, + "EmitResolverLinks.declarationLinks": unmemoizedContainer, + "EmitResolverLinks.declarationFileLinks": unmemoizedContainer, + "memoStore.TypeCount": unmemoizedCounter, + "memoStore.memberLayoutCount": unmemoizedCounter, + "memoStore.SymbolCount": unmemoizedCounter, + "memoStore.SignatureCount": unmemoizedCounter, + "memoStore.TotalInstantiationCount": unmemoizedCounter, + "memoStore.nanType": unmemoizedInterned, + "memoStore.parentLateBindableKinds": unmemoizedSyntax, + "memoStore.packagesMap": unmemoizedProgram, + "memoStore.ambientModulesOnce": unmemoizedProgram, + "memoStore.ambientModules": unmemoizedProgram, + "memoStore.memoStates": unmemoizedMemoState, + "memoStore.memoPendingCount": unmemoizedMemoState, + "memoStore.memoErrorStates": unmemoizedMemoState, + "memoStore.stateFilter": unmemoizedMemoState, + "memoStore.errorFilter": unmemoizedMemoState, + "memoStore.mu": unmemoizedMemoState, + "memoStore.diagnostics": unmemoizedChecking, + "memoStore.suggestionDiagnostics": unmemoizedChecking, + "memoStore.nonExistentPropertyNodes": unmemoizedChecking, + "memoStore.deferredDiagnosticCallbacks": unmemoizedChecking, + "SymbolReferenceLinks.referenceKinds": unmemoizedChecking, + "AliasSymbolLinks.referenced": unmemoizedMark, + "SourceFileLinks.typeChecked": unmemoizedChecking, + "SourceFileLinks.unusedChecked": unmemoizedChecking, + "SourceFileLinks.deferredNodes": unmemoizedChecking, + "SourceFileLinks.identifierCheckNodes": unmemoizedChecking, + "ValueSymbolLinks.target": unmemoizedCreated, + "ValueSymbolLinks.mapper": unmemoizedCreated, + "ValueSymbolLinks.extra": unmemoizedCreated, + "MappedSymbolLinks.keyType": unmemoizedCreated, + "MappedSymbolLinks.syntheticOrigin": unmemoizedCreated, + "DeferredSymbolLinks.parent": unmemoizedCreated, + "DeferredSymbolLinks.constituents": unmemoizedCreated, + "DeferredSymbolLinks.writeConstituents": unmemoizedCreated, + "ReverseMappedSymbolLinks.propertyType": unmemoizedCreated, + "ReverseMappedSymbolLinks.mappedType": unmemoizedCreated, + "ReverseMappedSymbolLinks.constraintType": unmemoizedCreated, + "ExportTypeLinks.target": unmemoizedCreated, + "ExportTypeLinks.originatingImport": unmemoizedCreated, + "SpreadLinks.leftSpread": unmemoizedCreated, + "SpreadLinks.rightSpread": unmemoizedCreated, + "JsxElementLinks.firstJSXTagInFile": unmemoizedSyntax, + "memoStore._jsxNamespace": unmemoizedSyntax, + "memoStore._jsxFactoryEntity": unmemoizedSyntax, + "SwitchStatementLinks.witnessesComputed": unmemoizedSyntax, + "SwitchStatementLinks.witnesses": unmemoizedSyntax, + "ArrayLiteralLinks.indicesComputed": unmemoizedSyntax, + "ArrayLiteralLinks.firstSpreadIndex": unmemoizedSyntax, + "ArrayLiteralLinks.lastSpreadIndex": unmemoizedSyntax, + "NodeLinks.declarationRequiresScopeChange": unmemoizedSyntax, + "SourceFileLinks.localJsxNamespace": unmemoizedSyntax, + "SourceFileLinks.localJsxFragmentNamespace": unmemoizedSyntax, + "SourceFileLinks.localJsxFactory": unmemoizedSyntax, + "SourceFileLinks.localJsxFragmentFactory": unmemoizedSyntax, + "JsxElementLinks.jsxFlags": unmemoizedFlagMemo, + "JsxElementLinks.jsxImplicitImportContainer": unmemoizedFlagMemo, + "flowLoopCacheEntry.t": unmemoizedLoopFixpoint, + "instantiationExpressionEntry.t": unmemoizedFlagMemo, + "ModuleSymbolLinks.resolvedExports": unmemoizedFlagMemo, + "ModuleSymbolLinks.typeOnlyExportStarMap": unmemoizedFlagMemo, + "LateBoundLinks.lateSymbol": unmemoizedGuarded, + "TypeAliasLinks.typeParameters": unmemoizedGuarded, + "MarkedAssignmentSymbolLinks.lastAssignmentPos": unmemoizedGuarded, + "MarkedAssignmentSymbolLinks.hasDefiniteAssignment": unmemoizedGuarded, + "EnumMemberLinks.value": unmemoizedGuarded, + "JSXLinks.importRef": unmemoizedEmit, + "DeclarationLinks.isVisible": unmemoizedEmit, + "DeclarationFileLinks.aliasesMarked": unmemoizedEmit, + "NodeBuilderLinks.serializedTypes": unmemoizedNodeBuilder, + "NodeBuilderLinks.fakeScopeForSignatureDeclaration": unmemoizedNodeBuilder, + "NodeBuilderSymbolLinks.specifierCache": unmemoizedNodeBuilder, + "SourceFileLinks.externalHelpersModule": unmemoizedChecking, + "SourceFileLinks.requestedExternalEmitHelpers": unmemoizedChecking, + "SourceFileLinks.externalHelperRequests": unmemoizedChecking, + "AliasSymbolLinks.typeOnlyDeclaration": unmemoizedGuarded, + "NodeLinks.flags": unmemoizedNodeFlags, +} diff --git a/tsc/internal/checker/emitresolver.go b/tsc/internal/checker/emitresolver.go index e4fad5c3f5b1f..9bbce09d442cc 100644 --- a/tsc/internal/checker/emitresolver.go +++ b/tsc/internal/checker/emitresolver.go @@ -398,7 +398,7 @@ func (r *EmitResolver) IsReferencedAliasDeclaration(node *ast.Node) bool { if aliasLinks.referenced { return true } - target := aliasLinks.aliasTarget + target := aliasLinks.aliasTarget.get(c) if target != nil && node.ModifierFlags()&ast.ModifierFlagsExport != 0 && c.getSymbolFlags(target)&ast.SymbolFlagsValue != 0 && (c.compilerOptions.ShouldPreserveConstEnums() || !isConstEnumOrConstEnumOnlyModule(target)) { diff --git a/tsc/internal/checker/flow.go b/tsc/internal/checker/flow.go index 303aedfefb5fb..328edb0023ddb 100644 --- a/tsc/internal/checker/flow.go +++ b/tsc/internal/checker/flow.go @@ -44,6 +44,7 @@ type FlowState struct { flowContainer *ast.Node refKey CacheHashKey depth int + limitHits int sharedFlowStart int reduceLabels []*ast.FlowReduceLabelData next *FlowState @@ -93,6 +94,11 @@ func (c *Checker) getFlowTypeOfReferenceEx(reference *ast.Node, declaredType *Ty f.sharedFlowStart = len(c.sharedFlows) c.flowInvocationCount++ evolvedType := c.getTypeAtFlowNode(f, flowNode).t + if f.limitHits != 0 { + // Give up on the whole reference, so its type does not depend on how + // deep the walk got before giving up. + evolvedType = c.errorType + } c.sharedFlows = c.sharedFlows[:f.sharedFlowStart] c.putFlowState(f) // When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation, @@ -111,14 +117,86 @@ func (c *Checker) getFlowTypeOfReferenceEx(reference *ast.Node, declaredType *Ty return resultType } +const maxFlowDepth = 2000 + func (c *Checker) getTypeAtFlowNode(f *FlowState, flow *ast.FlowNode) FlowType { - if f.depth == 2000 { + // A walk that is too deep from a node is also too deep from it with less + // depth left, so remember the failure to avoid repeating it for every + // reference after the first. Loops being analyzed and reduce labels make + // walks depend on more than the node and reference, so such walks are not + // remembered. + if len(c.flowDepthFailures) != 0 && c.isRememberedFlowDepthFailure(f, flow) { + return FlowType{t: c.errorType} + } + hits := f.limitHits + t := c.getTypeAtFlowNodeWorker(f, flow) + if f.limitHits != hits { + c.rememberFlowDepthFailure(f, flow) + } + return t +} + +// isRememberedFlowDepthFailure reports whether a walk from flow is known to +// be too deep for the depth left, reporting the failure if so. +// +//go:noinline +func (c *Checker) isRememberedFlowDepthFailure(f *FlowState, flow *ast.FlowNode) bool { + if len(c.flowLoopStack) != 0 || len(f.reduceLabels) != 0 || !c.hasFlowReferenceKey(f) { + return false + } + if b, ok := c.flowDepthFailures[FlowLoopKey{flowNode: flow, refKey: f.refKey}]; ok && maxFlowDepth-f.depth <= b { + f.limitHits++ + c.reportFlowControlError(f.reference) + return true + } + return false +} + +// rememberFlowDepthFailure remembers that a walk from flow was too deep for +// the depth left. +// +//go:noinline +func (c *Checker) rememberFlowDepthFailure(f *FlowState, flow *ast.FlowNode) { + if len(c.flowLoopStack) != 0 || len(f.reduceLabels) != 0 || !c.hasFlowReferenceKey(f) { + return + } + budget := maxFlowDepth - f.depth + key := FlowLoopKey{flowNode: flow, refKey: f.refKey} + if b, ok := c.flowDepthFailures[key]; !ok || b < budget { + if c.flowDepthFailures == nil { + c.flowDepthFailures = make(map[FlowLoopKey]int) + } + if c.logResults() { + // The walk may have depended on results a cycle in progress + // discards. + c.logRestore(func() { + if ok { + c.flowDepthFailures[key] = b + } else { + delete(c.flowDepthFailures, key) + } + }) + } + c.flowDepthFailures[key] = budget + } +} + +func (c *Checker) hasFlowReferenceKey(f *FlowState) bool { + if f.refKey.IsZero() { + f.refKey = c.getFlowReferenceKey(f) + } + return f.refKey != nonDottedNameCacheKey +} + +func (c *Checker) getTypeAtFlowNodeWorker(f *FlowState, flow *ast.FlowNode) FlowType { + if f.depth == maxFlowDepth { // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error // and give up on this reference. Each reference hits the limit on its own, so its type does not depend - // on which other references were analyzed first. + // on which other references were analyzed first; see getTypeAtFlowNode. if tr := c.tracer; tr != nil { tr.Instant(tracing.PhaseCheckTypes, "getTypeAtFlowNode_DepthLimit", map[string]any{"depth": f.depth}) } + f.limitHits++ c.reportFlowControlError(f.reference) return FlowType{t: c.errorType} } @@ -380,14 +458,14 @@ func (c *Checker) narrowType(f *FlowState, t *Type, expr *ast.Node, assumeTrue b case ast.KindIdentifier: // When narrowing a reference to a const variable, non-assigned parameter, or readonly property, we inline // up to five levels of aliased conditional expressions that are themselves declared as const variables. - if !c.isMatchingReference(f.reference, expr) && c.inlineLevel < 5 { + if !c.isMatchingReference(f.reference, expr) { symbol := c.getResolvedSymbol(expr) if c.isConstantVariable(symbol) { declaration := symbol.ValueDeclaration() - if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil && c.isConstantReference(f.reference) { - c.inlineLevel++ + if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil && c.isConstantReference(f.reference) && !c.depthLimitReached(&c.inlineLevel, 5) { + c.enterDepthLimit(&c.inlineLevel) result := c.narrowType(f, t, declaration.Initializer(), assumeTrue) - c.inlineLevel-- + c.inlineLevel.exit() return result } } @@ -845,11 +923,13 @@ func (c *Checker) getNarrowedType(t *Type, candidate *Type, assumeTrue bool, che return c.getNarrowedTypeWorker(t, candidate, assumeTrue, checkDerived) } key := NarrowedTypeKey{t, candidate, assumeTrue, checkDerived} - if narrowedType, ok := c.narrowedTypes[key]; ok { + if narrowedType, ok := c.narrowedTypes.get(c, key); ok { return narrowedType } + c.narrowedTypes.begin(c, key) narrowedType := c.getNarrowedTypeWorker(t, candidate, assumeTrue, checkDerived) - c.narrowedTypes[key] = narrowedType + c.narrowedTypes.set(c, key, narrowedType) + c.endMemo() return narrowedType } @@ -1328,11 +1408,24 @@ func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowT return FlowType{t: f.declaredType} } key := FlowLoopKey{flowNode: flow, refKey: f.refKey} + entry, _ := c.flowLoopCache.get(key) + if entry == nil { + entry = &flowLoopCacheEntry{} + c.flowLoopCache.intern(key, entry) + } // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. - if cached := c.flowLoopCache[key]; cached != nil { - return FlowType{t: cached} + if entry.t != nil { + c.observeFlagMemo(memoKeyOf(entry, memoFlowLoopType)) + return FlowType{t: entry.t} } + c.beginMemo(memoKeyOf(entry, memoFlowLoopType)) + result := c.getTypeAtFlowLoopLabelUncached(f, flow, key, entry) + c.endMemo() + return result +} + +func (c *Checker) getTypeAtFlowLoopLabelUncached(f *FlowState, flow *ast.FlowNode, key FlowLoopKey, entry *flowLoopCacheEntry) FlowType { // If this flow loop junction and reference are already being processed, return // the union of the types computed for each branch so far, marked as incomplete. // It is possible to see an empty array in cases where loops are nested and the @@ -1377,8 +1470,8 @@ func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowT // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up // the resulting type and bail out. - if cached := c.flowLoopCache[key]; cached != nil { - return FlowType{t: cached} + if entry.t != nil { + return FlowType{t: entry.t} } } antecedentTypes = core.AppendIfUnique(antecedentTypes, flowType.t) @@ -1401,7 +1494,10 @@ func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowT if firstAntecedentType.incomplete { return c.newFlowType(result, true /*incomplete*/) } - c.flowLoopCache[key] = result + // Kept even if a cycle in progress discards what it computed: analyzing + // the loop again after the cycle starts from wherever the next reference + // is, and may then reach a different fixed point. + entry.t = result return FlowType{t: result} } @@ -1512,11 +1608,11 @@ func getCandidateVariableDeclarationInitializer(node *ast.Node) *ast.Node { // and never escape the getFlowTypeOfReference function. func (c *Checker) getEvolvingArrayType(elementType *Type) *Type { key := CachedTypeKey{kind: CachedTypeKindEvolvingArrayType, typeId: elementType.id} - result := c.cachedTypes[key] + result, _ := c.cachedTypes.get(c, key) if result == nil { result = c.newObjectType(ObjectFlagsEvolvingArray, nil) result.AsEvolvingArrayType().elementType = elementType - c.cachedTypes[key] = result + c.cachedTypes.intern(key, result) } return result } @@ -1575,10 +1671,7 @@ func (c *Checker) finalizeEvolvingArrayType(t *Type) *Type { } func (c *Checker) getFinalArrayType(t *EvolvingArrayType) *Type { - if t.finalArrayType == nil { - t.finalArrayType = c.createFinalArrayType(t.elementType) - } - return t.finalArrayType + return c.memoCompute(&t.finalArrayType, func() *Type { return c.createFinalArrayType(t.elementType) }) } func (c *Checker) createFinalArrayType(elementType *Type) *Type { @@ -1948,18 +2041,21 @@ func isCoercibleUnderDoubleEquals(source *Type, target *Type) bool { func (c *Checker) isExhaustiveSwitchStatement(node *ast.Node) bool { links := c.switchStatementLinks.Get(node) - if links.exhaustiveState == ExhaustiveStateUnknown { + switch links.exhaustiveState.get(c) { + case ExhaustiveStateUnknown: + links.exhaustiveState.begin(c) // Indicate resolution is in process - links.exhaustiveState = ExhaustiveStateComputing + links.exhaustiveState.set(c, ExhaustiveStateComputing) isExhaustive := c.computeExhaustiveSwitchStatement(node) - if links.exhaustiveState == ExhaustiveStateComputing { - links.exhaustiveState = core.IfElse(isExhaustive, ExhaustiveStateTrue, ExhaustiveStateFalse) + if links.exhaustiveState.get(c) == ExhaustiveStateComputing { + links.exhaustiveState.set(c, core.IfElse(isExhaustive, ExhaustiveStateTrue, ExhaustiveStateFalse)) } - } else if links.exhaustiveState == ExhaustiveStateComputing { + c.endMemo() + case ExhaustiveStateComputing: // Resolve circularity to false - links.exhaustiveState = ExhaustiveStateFalse + links.exhaustiveState.set(c, ExhaustiveStateFalse) } - return links.exhaustiveState == ExhaustiveStateTrue + return links.exhaustiveState.get(c) == ExhaustiveStateTrue } func (c *Checker) computeExhaustiveSwitchStatement(node *ast.Node) bool { @@ -2041,16 +2137,17 @@ func (c *Checker) getNotEqualFactsFromTypeofSwitch(start int, end int, witnesses func (c *Checker) getSwitchClauseTypes(node *ast.Node) []*Type { links := c.switchStatementLinks.Get(node) - if !links.switchTypesComputed { + if links.switchTypes.get(c) == nil { + links.switchTypes.begin(c) clauses := node.AsSwitchStatement().CaseBlock.AsCaseBlock().Clauses.Nodes types := make([]*Type, len(clauses)) for i, clause := range clauses { types[i] = c.getTypeOfSwitchClause(clause) } - links.switchTypes = types - links.switchTypesComputed = true + links.switchTypes.set(c, types) + c.endMemo() } - return links.switchTypes + return links.switchTypes.get(c) } func (c *Checker) getTypeOfSwitchClause(clause *ast.Node) *Type { @@ -2062,8 +2159,9 @@ func (c *Checker) getTypeOfSwitchClause(clause *ast.Node) *Type { func (c *Checker) getEffectsSignature(node *ast.Node) *Signature { links := c.signatureLinks.Get(node) - signature := links.effectsSignature + signature := links.effectsSignature.get(c) if signature == nil { + links.effectsSignature.begin(c) // A call expression parented by an expression statement is a potential assertion. Other call // expressions are potential type predicate function calls. In order to avoid triggering // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call @@ -2095,7 +2193,8 @@ func (c *Checker) getEffectsSignature(node *ast.Node) *Signature { if !(signature != nil && c.hasTypePredicateOrNeverReturnType(signature)) { signature = c.unknownSignature } - links.effectsSignature = signature + links.effectsSignature.set(c, signature) + c.endMemo() } if signature == c.unknownSignature { return nil @@ -2170,10 +2269,11 @@ func (c *Checker) getTypeOfDottedName(node *ast.Node, diagnostic *ast.Diagnostic func (c *Checker) getExplicitTypeOfSymbol(symbol *ast.Symbol, diagnostic *ast.Diagnostic) *Type { symbol = c.resolveSymbol(symbol) - if !c.resolvingExplicitTypeOfSymbol.AddIfAbsent(symbol) { + if c.isResolvingAnywhere(symbol, TypeSystemPropertyNameExplicitType) { return nil } - defer c.resolvingExplicitTypeOfSymbol.Delete(symbol) + c.beginTypeResolution(symbol, TypeSystemPropertyNameExplicitType, nil) + defer c.popTypeResolution() if symbol.Flags()&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsValueModule) != 0 { return c.getTypeOfSymbol(symbol) } @@ -2278,7 +2378,7 @@ func (c *Checker) getTypeOfInitializer(node *ast.Node) *Type { // from its initializer, we'll already have cached the type. Otherwise we compute it now // without caching such that transient types are reflected. if c.typeNodeLinks.Has(node) { - t := c.typeNodeLinks.Get(node).resolvedType + t := c.typeNodeLinks.Get(node).resolvedType.get(c) if t != nil { return t } @@ -2420,10 +2520,12 @@ func (c *Checker) getAssignmentReducedType(declaredType *Type, assignedType *Typ return assignedType } key := AssignmentReducedKey{id1: declaredType.id, id2: assignedType.id} - result := c.assignmentReducedTypes[key] + result, _ := c.assignmentReducedTypes.get(c, key) if result == nil { + c.assignmentReducedTypes.begin(c, key) result = c.getAssignmentReducedTypeWorker(declaredType, assignedType) - c.assignmentReducedTypes[key] = result + c.assignmentReducedTypes.set(c, key, result) + c.endMemo() } return result } @@ -2530,24 +2632,30 @@ func (c *Checker) isReachableFlowNode(flow *ast.FlowNode) bool { f := c.getFlowState() result := c.isReachableFlowNodeWorker(f, flow, false /*noCacheCheck*/) c.putFlowState(f) - c.lastFlowNode = flow - c.lastFlowNodeReachable = result + // The last result is reused without accounting for reading it, so it is + // only kept where nothing computed since could be discarded. + if !c.logResults() { + c.lastFlowNode = flow + c.lastFlowNodeReachable = result + } return result } func (c *Checker) isReachableFlowNodeWorker(f *FlowState, flow *ast.FlowNode, noCacheCheck bool) bool { for { - if flow == c.lastFlowNode { + if flow == c.lastFlowNode && !c.observing() { return c.lastFlowNodeReachable } flags := flow.Flags if flags&ast.FlowFlagsShared != 0 { if !noCacheCheck && len(f.reduceLabels) == 0 { - if reachable, ok := c.flowNodeReachable[flow]; ok { + if reachable, ok := c.flowNodeReachable.get(c, flow); ok { return reachable } + c.flowNodeReachable.begin(c, flow) reachable := c.isReachableFlowNodeWorker(f, flow, true /*noCacheCheck*/) - c.flowNodeReachable[flow] = reachable + c.flowNodeReachable.set(c, flow, reachable) + c.endMemo() return reachable } noCacheCheck = false @@ -2629,11 +2737,13 @@ func (c *Checker) isPostSuperFlowNodeWorker(f *FlowState, flow *ast.FlowNode, no flags := flow.Flags if flags&ast.FlowFlagsShared != 0 { if !noCacheCheck { - if postSuper, ok := c.flowNodePostSuper[flow]; ok { + if postSuper, ok := c.flowNodePostSuper.get(c, flow); ok { return postSuper } + c.flowNodePostSuper.begin(c, flow) postSuper := c.isPostSuperFlowNodeWorker(f, flow, true /*noCacheCheck*/) - c.flowNodePostSuper[flow] = postSuper + c.flowNodePostSuper.set(c, flow, postSuper) + c.endMemo() } noCacheCheck = false } @@ -2692,18 +2802,16 @@ func (c *Checker) ensureAssignmentsMarked(symbol *ast.Symbol) { if parent == nil { return } - links := c.nodeLinks.Get(parent) - if links.flags&NodeCheckFlagsAssignmentsMarked == 0 { - links.flags |= NodeCheckFlagsAssignmentsMarked + c.assignmentsMarked.once(c, parent, func() { if !c.hasParentWithAssignmentsMarked(parent) { c.markNodeAssignments(parent) } - } + }) } func (c *Checker) hasParentWithAssignmentsMarked(node *ast.Node) bool { return ast.FindAncestor(node.Parent, func(node *ast.Node) bool { - return ast.IsFunctionOrSourceFile(node) && c.nodeLinks.Get(node).flags&NodeCheckFlagsAssignmentsMarked != 0 + return ast.IsFunctionOrSourceFile(node) && c.assignmentsMarked.has(c, node) }) != nil } diff --git a/tsc/internal/checker/grammarchecks.go b/tsc/internal/checker/grammarchecks.go index f8997b8e361e1..afb6d2e63237d 100644 --- a/tsc/internal/checker/grammarchecks.go +++ b/tsc/internal/checker/grammarchecks.go @@ -2023,13 +2023,20 @@ func (c *Checker) checkGrammarSourceFile(node *ast.SourceFile) bool { return node.Flags&ast.NodeFlagsAmbient != 0 && c.checkGrammarTopLevelElementsForRequiredDeclareModifier(node) } +func (c *Checker) reportStatementInAmbientContext(links *NodeLinks, node *ast.Node, message *diagnostics.Message) bool { + links.hasReportedStatementInAmbientContext.begin(c) + reported := c.grammarErrorOnFirstToken(node, message) + links.hasReportedStatementInAmbientContext.set(c, reported) + c.endMemo() + return reported +} + func (c *Checker) checkGrammarStatementInAmbientContext(node *ast.Node) bool { if node.Flags&ast.NodeFlagsAmbient != 0 { // Find containing block which is either Block, ModuleBlock, SourceFile links := c.nodeLinks.Get(node) - if !links.hasReportedStatementInAmbientContext && (ast.IsFunctionLike(node.Parent) || ast.IsAccessor(node.Parent)) { - links.hasReportedStatementInAmbientContext = c.grammarErrorOnFirstToken(node, diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts) - return links.hasReportedStatementInAmbientContext + if !links.hasReportedStatementInAmbientContext.get(c) && (ast.IsFunctionLike(node.Parent) || ast.IsAccessor(node.Parent)) { + return c.reportStatementInAmbientContext(links, node, diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts) } // We are either parented by another statement, or some sort of block. @@ -2040,9 +2047,8 @@ func (c *Checker) checkGrammarStatementInAmbientContext(node *ast.Node) bool { if node.Parent.Kind == ast.KindBlock || node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile { links := c.nodeLinks.Get(node.Parent) // Check if the containing block ever report this error - if !links.hasReportedStatementInAmbientContext { - links.hasReportedStatementInAmbientContext = c.grammarErrorOnFirstToken(node, diagnostics.Statements_are_not_allowed_in_ambient_contexts) - return links.hasReportedStatementInAmbientContext + if !links.hasReportedStatementInAmbientContext.get(c) { + return c.reportStatementInAmbientContext(links, node, diagnostics.Statements_are_not_allowed_in_ambient_contexts) } } else { // We must be parented by a statement. If so, there's no need diff --git a/tsc/internal/checker/inference.go b/tsc/internal/checker/inference.go index 8b0cc87aedef4..8a1cdbe472d9a 100644 --- a/tsc/internal/checker/inference.go +++ b/tsc/internal/checker/inference.go @@ -399,7 +399,7 @@ func (c *Checker) inferFromMatchingTypes(n *InferenceState, sources []*Type, tar // when inferring from string[] | string[][] to T[] | T[][], the inference of string we make from // relating string[][] to T[][] is of higher quality than the inference of string[] we make relating // string[][] to T[]. - slices.SortFunc(matchedTargets, compareTypesAndDepth) + slices.SortFunc(matchedTargets, c.compareTypesAndDepth) for _, t := range matchedTargets { for _, s := range matchedSources { if matches(c, s, t) { @@ -418,9 +418,9 @@ func (c *Checker) inferFromMatchingTypes(n *InferenceState, sources []*Type, tar } // Compare two types first by depth and then by the regular type ordering. -func compareTypesAndDepth(t1, t2 *Type) int { - d1 := getTypeDepth(t1, 3) - d2 := getTypeDepth(t2, 3) +func (c *Checker) compareTypesAndDepth(t1, t2 *Type) int { + d1 := c.getTypeDepth(t1, 3) + d2 := c.getTypeDepth(t2, 3) if d1 != d2 { return d2 - d1 // Largest depth sorts first } @@ -431,27 +431,27 @@ func compareTypesAndDepth(t1, t2 *Type) int { // and type references, the depth is one plus the largest type argument depth. For union and // intersection types, the depth is the largest constituent type depth. For all other types, // the depth is zero. The maximum depth limits infinite recursion of circular types. -func getTypeDepth(t *Type, maxDepth int) int { +func (c *Checker) getTypeDepth(t *Type, maxDepth int) int { if maxDepth != 0 { if t.alias != nil && len(t.alias.typeArguments) != 0 { - return getTypeListDepth(t.alias.typeArguments, maxDepth-1) + 1 + return c.getTypeListDepth(t.alias.typeArguments, maxDepth-1) + 1 } if t.objectFlags&ObjectFlagsReference != 0 { - if typeArguments := t.checker.getTypeArguments(t); len(typeArguments) != 0 { - return getTypeListDepth(typeArguments, maxDepth-1) + 1 + if typeArguments := c.getTypeArguments(t); len(typeArguments) != 0 { + return c.getTypeListDepth(typeArguments, maxDepth-1) + 1 } } if t.flags&TypeFlagsUnionOrIntersection != 0 { - return getTypeListDepth(t.Types(), maxDepth) + return c.getTypeListDepth(t.Types(), maxDepth) } } return 0 } -func getTypeListDepth(types []*Type, maxDepth int) int { +func (c *Checker) getTypeListDepth(types []*Type, maxDepth int) int { depth := 0 for _, t := range types { - depth = max(depth, getTypeDepth(t, maxDepth)) + depth = max(depth, c.getTypeDepth(t, maxDepth)) } return depth } @@ -984,7 +984,7 @@ func (c *Checker) inferToMappedType(n *InferenceState, source *Type, target *Typ if constraintType.flags&TypeFlagsTypeParameter != 0 { // We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type // parameter. First infer from 'keyof S' to K. - c.inferWithPriority(n, c.getIndexTypeEx(source, core.IfElse(c.patternForType[source] != nil, IndexFlagsNoIndexSignatures, IndexFlagsNone)), constraintType, InferencePriorityMappedTypeConstraint) + c.inferWithPriority(n, c.getIndexTypeEx(source, core.IfElse(c.patternForType.has(source), IndexFlagsNoIndexSignatures, IndexFlagsNone)), constraintType, InferencePriorityMappedTypeConstraint) // If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X }, // where K extends keyof T, we make the same inferences as for a homomorphic mapped type // { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a @@ -1014,11 +1014,13 @@ func (c *Checker) inferToMappedType(n *InferenceState, source *Type, target *Typ // variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). func (c *Checker) inferTypeForHomomorphicMappedType(source *Type, target *Type, constraint *Type) *Type { key := ReverseMappedTypeKey{sourceId: source.id, targetId: target.id, constraintId: constraint.id} - if cached := c.reverseHomomorphicMappedCache[key]; cached != nil { + if cached, _ := c.reverseHomomorphicMappedCache.get(c, key); cached != nil { return cached } + c.reverseHomomorphicMappedCache.begin(c, key) t := c.createReverseMappedType(source, target, constraint) - c.reverseHomomorphicMappedCache[key] = t + c.reverseHomomorphicMappedCache.set(c, key, t) + c.endMemo() return t } @@ -1076,9 +1078,13 @@ func (c *Checker) isPartiallyInferableType(t *Type) bool { func (c *Checker) inferReverseMappedType(source *Type, target *Type, constraint *Type) *Type { key := ReverseMappedTypeKey{sourceId: source.id, targetId: target.id, constraintId: constraint.id} - if cached, ok := c.reverseMappedCache[key]; ok { + if cached, ok := c.reverseMappedCache.get(c, key); ok { return core.OrElse(cached, c.unknownType) } + c.reverseMappedCache.begin(c, key) + if len(c.reverseMappedSourceStack) == 0 { + c.reverseMappedStart = int32(len(c.resolutions) - 1) + } c.reverseMappedSourceStack = append(c.reverseMappedSourceStack, source) c.reverseMappedTargetStack = append(c.reverseMappedTargetStack, target) saveExpandingFlags := c.reverseExpandingFlags @@ -1091,11 +1097,16 @@ func (c *Checker) inferReverseMappedType(source *Type, target *Type, constraint var t *Type if c.reverseExpandingFlags != ExpandingFlagsBoth { t = c.inferReverseMappedTypeWorker(source, target, constraint) + } else { + // Stopping depends on the enclosing reverse mappings, not just on key, so + // only the outermost one, which has none, may keep a result. + c.markCircular(int(c.reverseMappedStart)) } c.reverseMappedSourceStack = c.reverseMappedSourceStack[:len(c.reverseMappedSourceStack)-1] c.reverseMappedTargetStack = c.reverseMappedTargetStack[:len(c.reverseMappedTargetStack)-1] c.reverseExpandingFlags = saveExpandingFlags - c.reverseMappedCache[key] = t + c.reverseMappedCache.set(c, key, t) + c.endMemo() return t } @@ -1155,16 +1166,16 @@ func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { } members.Set(prop.Name(), inferredProp) } - c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) + c.setStructuredTypeData(t, members, nil, nil, indexInfos) } func (c *Checker) getTypeOfReverseMappedSymbol(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { + if links.resolvedType.get(c) == nil { reverseLinks := c.ReverseMappedSymbolLinks.Get(symbol) - links.resolvedType = core.OrElse(c.inferReverseMappedType(reverseLinks.propertyType, reverseLinks.mappedType, reverseLinks.constraintType), c.unknownType) + links.resolvedType.set(c, core.OrElse(c.inferReverseMappedType(reverseLinks.propertyType, reverseLinks.mappedType, reverseLinks.constraintType), c.unknownType)) } - return links.resolvedType + return links.resolvedType.get(c) } // If the original mapped type had an intersection constraint we extract its components, @@ -1250,7 +1261,7 @@ func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type { } name := getStringLiteralValue(t) literalProp := c.newSymbol(ast.SymbolFlagsProperty, name) - c.valueSymbolLinks.Get(literalProp).resolvedType = c.anyType + c.valueSymbolLinks.Get(literalProp).resolvedType.setIdentity(c, c.anyType) if t.symbol != nil { literalProp.SetDeclarations(t.symbol.Declarations()) literalProp.SetValueDeclaration(t.symbol.ValueDeclaration()) diff --git a/tsc/internal/checker/jsx.go b/tsc/internal/checker/jsx.go index 1054f4d46f8c5..5525a6ac668bd 100644 --- a/tsc/internal/checker/jsx.go +++ b/tsc/internal/checker/jsx.go @@ -32,11 +32,11 @@ const ( ) type JsxElementLinks struct { - jsxFlags JsxFlags // Flags for the JSX element - resolvedJsxElementAttributesType *Type // Resolved element attributes type of a JSX opening-like element - jsxNamespace *ast.Symbol // Resolved JSX namespace symbol for this node - jsxImplicitImportContainer *ast.Symbol // Resolved module symbol the implicit JSX import of this file should refer to - firstJSXTagInFile *ast.Node // The first JSX tag in the file + jsxFlags JsxFlags // Flags for the JSX element + resolvedJsxElementAttributesType memo[*Type] // Resolved element attributes type of a JSX opening-like element + jsxNamespace memo[*ast.Symbol] // Resolved JSX namespace symbol for this node + jsxImplicitImportContainer *ast.Symbol // Resolved module symbol the implicit JSX import of this file should refer to + firstJSXTagInFile *ast.Node // The first JSX tag in the file } var JsxNames = struct { @@ -265,9 +265,10 @@ func (c *Checker) getContextualTypeForChildJsxExpression(node *ast.Node, child * func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, contextualType *Type) *Type { key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id} - if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil { + if discriminated, _ := c.discriminatedContextualTypes.get(c, key); discriminated != nil { return discriminated } + c.discriminatedContextualTypes.begin(c, key) jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) discriminantProperties := core.Filter(node.Properties(), func(p *ast.Node) bool { symbol := p.Symbol() @@ -289,7 +290,8 @@ func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, cont }) discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} discriminated := c.discriminateTypeByDiscriminableItems(contextualType, discriminator) - c.discriminatedContextualTypes[key] = discriminated + c.discriminatedContextualTypes.set(c, key, discriminated) + c.endMemo() return discriminated } @@ -500,15 +502,22 @@ func (c *Checker) getSuggestedSymbolForNonexistentJSXAttribute(name string, cont func (c *Checker) getJSXFragmentType(node *ast.Node) *Type { // An opening fragment is required in order for `getJsxNamespace` to give the fragment factory links := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(node)) - if links.jsxFragmentType != nil { - return links.jsxFragmentType + if t := links.jsxFragmentType.get(c); t != nil { + return t } + links.jsxFragmentType.begin(c) + t := c.getJSXFragmentTypeUncached(node) + links.jsxFragmentType.set(c, t) + c.endMemo() + return t +} + +func (c *Checker) getJSXFragmentTypeUncached(node *ast.Node) *Type { jsxFragmentFactoryName := c.getJsxNamespace(node) // #38720/60122, allow null as jsxFragmentFactory shouldResolveFactoryReference := (c.compilerOptions.Jsx == core.JsxEmitReact || c.compilerOptions.JsxFragmentFactory != "") && jsxFragmentFactoryName != "null" if !shouldResolveFactoryReference { - links.jsxFragmentType = c.anyType - return links.jsxFragmentType + return c.anyType } jsxFactorySymbol := c.getJsxNamespaceContainerForImplicitImport(node) if jsxFactorySymbol == nil { @@ -520,12 +529,10 @@ func (c *Checker) getJSXFragmentType(node *ast.Node) *Type { jsxFactorySymbol = c.resolveName(node, jsxFragmentFactoryName, flags, diagnostics.Using_JSX_fragments_requires_fragment_factory_0_to_be_in_scope_but_it_could_not_be_found, true /*isUse*/, false /*excludeGlobals*/) } if jsxFactorySymbol == nil { - links.jsxFragmentType = c.errorType - return links.jsxFragmentType + return c.errorType } if jsxFactorySymbol.Name() == ReactNames.Fragment { - links.jsxFragmentType = c.getTypeOfSymbol(jsxFactorySymbol) - return links.jsxFragmentType + return c.getTypeOfSymbol(jsxFactorySymbol) } resolvedAlias := jsxFactorySymbol if jsxFactorySymbol.Flags()&ast.SymbolFlagsAlias != 0 { @@ -535,11 +542,9 @@ func (c *Checker) getJSXFragmentType(node *ast.Node) *Type { reactExports := c.getExportsOfSymbol(resolvedAlias) typeSymbol := c.getSymbol(reactExports, ReactNames.Fragment, ast.SymbolFlagsBlockScopedVariable) if typeSymbol != nil { - links.jsxFragmentType = c.getTypeOfSymbol(typeSymbol) - } else { - links.jsxFragmentType = c.errorType + return c.getTypeOfSymbol(typeSymbol) } - return links.jsxFragmentType + return c.errorType } func (c *Checker) resolveJsxOpeningLikeElement(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { @@ -748,7 +753,7 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme attributeSymbol.SetValueDeclaration(member.ValueDeclaration()) } links := c.valueSymbolLinks.Get(attributeSymbol) - links.resolvedType = exprType + links.resolvedType.set(c, exprType) links.target = member attributesTable.Set(attributeSymbol.Name(), attributeSymbol) if allAttributesTable != nil { @@ -836,11 +841,11 @@ func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeEleme links := c.valueSymbolLinks.Get(childrenPropSymbol) switch { case len(childTypes) == 1: - links.resolvedType = childTypes[0] + links.resolvedType.set(c, childTypes[0]) case childrenContextualType != nil && someType(childrenContextualType, c.isTupleLikeType): - links.resolvedType = c.createTupleType(childTypes) + links.resolvedType.set(c, c.createTupleType(childTypes)) default: - links.resolvedType = c.createArrayType(c.getUnionType(childTypes)) + links.resolvedType.set(c, c.createArrayType(c.getUnionType(childTypes))) } // Fake up a property declaration for the children childrenPropSymbol.SetValueDeclaration(c.factory.NewPropertySignatureDeclaration(nil, c.factory.NewIdentifier(jsxChildrenPropertyName), nil /*postfixToken*/, nil /*type*/, nil /*initializer*/)) @@ -1178,7 +1183,7 @@ func (c *Checker) createSignatureForJSXIntrinsic(node *ast.Node, result *Type) * // returnNode := typeSymbol && c.nodeBuilder.symbolToEntityName(typeSymbol, ast.SymbolFlagsType, node) // declaration := factory.createFunctionTypeNode(nil, []ParameterDeclaration{factory.createParameterDeclaration(nil, nil /*dotDotDotToken*/, "props", nil /*questionToken*/, c.nodeBuilder.typeToTypeNode(result, node))}, ifElse(returnNode != nil, factory.createTypeReferenceNode(returnNode, nil /*typeArguments*/), factory.createKeywordTypeNode(ast.KindAnyKeyword))) parameterSymbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "props") - c.valueSymbolLinks.Get(parameterSymbol).resolvedType = result + c.valueSymbolLinks.Get(parameterSymbol).resolvedType.set(c, result) return c.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{parameterSymbol}, elementType, nil, 1) } @@ -1188,23 +1193,31 @@ func (c *Checker) createSignatureForJSXIntrinsic(node *ast.Node, result *Type) * func (c *Checker) getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node *ast.Node) *Type { debug.Assert(isJsxIntrinsicTagName(node.TagName())) links := c.jsxElementLinks.Get(node) - if links.resolvedJsxElementAttributesType != nil { - return links.resolvedJsxElementAttributesType + if t := links.resolvedJsxElementAttributesType.get(c); t != nil { + return t } + links.resolvedJsxElementAttributesType.begin(c) + result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElementUncached(node, links) + c.endMemo() + return result +} + +func (c *Checker) getIntrinsicAttributesTypeFromJsxOpeningLikeElementUncached(node *ast.Node, links *JsxElementLinks) *Type { + if t := links.resolvedJsxElementAttributesType.get(c); t != nil { + return t + } + result := c.errorType symbol := c.getIntrinsicTagSymbol(node) if links.jsxFlags&JsxFlagsIntrinsicNamedElement != 0 { - links.resolvedJsxElementAttributesType = core.OrElse(c.getTypeOfSymbol(symbol), c.errorType) - return links.resolvedJsxElementAttributesType - } - if links.jsxFlags&JsxFlagsIntrinsicIndexedElement != 0 { + result = core.OrElse(c.getTypeOfSymbol(symbol), c.errorType) + } else if links.jsxFlags&JsxFlagsIntrinsicIndexedElement != 0 { indexInfo := c.getApplicableIndexInfoForName(c.getJsxType(JsxNames.IntrinsicElements, node), node.TagName().Text()) if indexInfo != nil { - links.resolvedJsxElementAttributesType = indexInfo.valueType - return links.resolvedJsxElementAttributesType + result = indexInfo.valueType } } - links.resolvedJsxElementAttributesType = c.errorType - return links.resolvedJsxElementAttributesType + links.resolvedJsxElementAttributesType.set(c, result) + return result } // Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic @@ -1212,9 +1225,22 @@ func (c *Checker) getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node *ast. // string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). // May also return unknownSymbol if both of these lookups fail. func (c *Checker) getIntrinsicTagSymbol(node *ast.Node) *ast.Symbol { + c.symbolNodeLinks.Get(node).resolvedSymbol.begin(c) + result := c.getIntrinsicTagSymbolUncached(node) + c.endMemo() + return result +} + +// setIntrinsicJsxFlag records how the intrinsic tag symbol of node was found, +// as part of resolving that symbol. +func (c *Checker) setIntrinsicJsxFlag(node *ast.Node, links *SymbolNodeLinks, flag JsxFlags) { + setFlagMemoBits(c, links.resolvedSymbol.key(), &c.jsxElementLinks.Get(node).jsxFlags, JsxFlagsIntrinsicNamedElement|JsxFlagsIntrinsicIndexedElement, flag) +} + +func (c *Checker) getIntrinsicTagSymbolUncached(node *ast.Node) *ast.Symbol { links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol != nil { - return links.resolvedSymbol + if links.resolvedSymbol.get(c) != nil { + return links.resolvedSymbol.get(c) } intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, node) if !c.isErrorType(intrinsicElementsType) { @@ -1226,32 +1252,32 @@ func (c *Checker) getIntrinsicTagSymbol(node *ast.Node) *ast.Symbol { propName := tagName.Text() intrinsicProp := c.getPropertyOfType(intrinsicElementsType, propName) if intrinsicProp != nil { - c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicNamedElement - links.resolvedSymbol = intrinsicProp - return links.resolvedSymbol + c.setIntrinsicJsxFlag(node, links, JsxFlagsIntrinsicNamedElement) + links.resolvedSymbol.set(c, intrinsicProp) + return links.resolvedSymbol.get(c) } // Intrinsic string indexer case indexSymbol := c.getApplicableIndexSymbol(intrinsicElementsType, c.getStringLiteralType(propName)) if indexSymbol != nil { - c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement - links.resolvedSymbol = indexSymbol - return links.resolvedSymbol + c.setIntrinsicJsxFlag(node, links, JsxFlagsIntrinsicIndexedElement) + links.resolvedSymbol.set(c, indexSymbol) + return links.resolvedSymbol.get(c) } if c.getTypeOfPropertyOrIndexSignatureOfType(intrinsicElementsType, propName) != nil { - c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement - links.resolvedSymbol = intrinsicElementsType.symbol - return links.resolvedSymbol + c.setIntrinsicJsxFlag(node, links, JsxFlagsIntrinsicIndexedElement) + links.resolvedSymbol.set(c, intrinsicElementsType.symbol) + return links.resolvedSymbol.get(c) } // Wasn't found c.error(node, diagnostics.Property_0_does_not_exist_on_type_1, tagName.Text(), "JSX."+JsxNames.IntrinsicElements) - links.resolvedSymbol = c.unknownSymbol - return links.resolvedSymbol + links.resolvedSymbol.set(c, c.unknownSymbol) + return links.resolvedSymbol.get(c) } if c.noImplicitAny { c.error(node, diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements) } - links.resolvedSymbol = c.unknownSymbol - return links.resolvedSymbol + links.resolvedSymbol.set(c, c.unknownSymbol) + return links.resolvedSymbol.get(c) } func (c *Checker) getJsxStatelessElementTypeAt(location *ast.Node) *Type { @@ -1302,14 +1328,28 @@ func (c *Checker) getJsxType(name string, location *ast.Node) *Type { } func (c *Checker) getJsxNamespaceAt(location *ast.Node) *ast.Symbol { - var links *JsxElementLinks - if location != nil { - links = c.jsxElementLinks.Get(location) + if location == nil { + return c.getJsxNamespaceAtUncached(location, nil) } - if links != nil && links.jsxNamespace != nil && links.jsxNamespace != c.unknownSymbol { - return links.jsxNamespace + links := c.jsxElementLinks.Get(location) + if s := links.jsxNamespace.get(c); s != nil && s != c.unknownSymbol { + return s } - if links == nil || links.jsxNamespace != c.unknownSymbol { + links.jsxNamespace.begin(c) + result := c.getJsxNamespaceAtUncached(location, links) + c.endMemo() + return result +} + +func (c *Checker) getJsxNamespaceAtUncached(location *ast.Node, links *JsxElementLinks) *ast.Symbol { + var cached *ast.Symbol + if links != nil { + cached = links.jsxNamespace.get(c) + if cached != nil && cached != c.unknownSymbol { + return cached + } + } + if cached != c.unknownSymbol { resolvedNamespace := c.getJsxNamespaceContainerForImplicitImport(location) if resolvedNamespace == nil || resolvedNamespace == c.unknownSymbol { namespaceName := c.getJsxNamespace(location) @@ -1319,13 +1359,13 @@ func (c *Checker) getJsxNamespaceAt(location *ast.Node) *ast.Symbol { candidate := c.resolveSymbol(c.getSymbol(c.getExportsOfSymbol(c.resolveSymbol(resolvedNamespace)), JsxNames.JSX, ast.SymbolFlagsNamespace)) if candidate != nil && candidate != c.unknownSymbol { if links != nil { - links.jsxNamespace = candidate + links.jsxNamespace.set(c, candidate) } return candidate } } if links != nil { - links.jsxNamespace = c.unknownSymbol + links.jsxNamespace.set(c, c.unknownSymbol) } } // JSX global fallback @@ -1450,8 +1490,16 @@ func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node) file := ast.GetSourceFileOfNode(location) links := c.jsxElementLinks.Get(file.AsNode()) if links.jsxImplicitImportContainer != nil { + c.observeFlagMemo(memoKeyOf(links, memoJsxImplicitImportContainer)) return core.IfElse(links.jsxImplicitImportContainer == c.unknownSymbol, nil, links.jsxImplicitImportContainer) } + c.beginMemo(memoKeyOf(links, memoJsxImplicitImportContainer)) + result := c.getJsxNamespaceContainerForImplicitImportUncached(file, links) + c.endMemo() + return result +} + +func (c *Checker) getJsxNamespaceContainerForImplicitImportUncached(file *ast.SourceFile, links *JsxElementLinks) *ast.Symbol { canonicalErrorTag := links.firstJSXTagInFile if canonicalErrorTag == nil { var visit ast.Visitor @@ -1479,7 +1527,7 @@ func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node) if mod != nil && mod != c.unknownSymbol { result = c.getMergedSymbol(c.resolveSymbol(mod)) } - links.jsxImplicitImportContainer = core.OrElse(result, c.unknownSymbol) + c.setFlagMemoValue(memoKeyOf(links, memoJsxImplicitImportContainer), &links.jsxImplicitImportContainer, core.OrElse(result, c.unknownSymbol)) return result } diff --git a/tsc/internal/checker/links.go b/tsc/internal/checker/links.go index c21981cae9d80..e7a92438685bf 100644 --- a/tsc/internal/checker/links.go +++ b/tsc/internal/checker/links.go @@ -31,8 +31,13 @@ func (s *nodeLinkStore[V]) Has(node *ast.Node) bool { return s.TryGet(node) != nil } +// TryGet returns the links of node, or nil if it has none. A node without +// an id has no links, and looking it up does not give it one. func (s *nodeLinkStore[V]) TryGet(node *ast.Node) *V { - id := uint64(s.gen.GetNodeId(node)) + id := uint64(ast.PeekNodeId(node)) + if id == 0 { + return nil + } if id >= ast.BlockIdOffset && id < ast.BlockIdOffset+maxPageLinkCount { return s.pages.TryGet(id - ast.BlockIdOffset) } @@ -57,8 +62,13 @@ func (s *symbolLinkStore[V]) Has(symbol *ast.Symbol) bool { return s.TryGet(symbol) != nil } +// TryGet returns the links of symbol, or nil if it has none. A symbol +// without an id has no links, and looking it up does not give it one. func (s *symbolLinkStore[V]) TryGet(symbol *ast.Symbol) *V { - id := uint64(s.gen.GetSymbolId(symbol)) + id := uint64(ast.PeekSymbolId(symbol)) + if id == 0 { + return nil + } if id >= ast.BlockIdOffset && id < ast.BlockIdOffset+maxPageLinkCount { return s.pages.TryGet(id - ast.BlockIdOffset) } diff --git a/tsc/internal/checker/memo.go b/tsc/internal/checker/memo.go new file mode 100644 index 0000000000000..8fbd18aa4d0f3 --- /dev/null +++ b/tsc/internal/checker/memo.go @@ -0,0 +1,2080 @@ +package checker + +import ( + "iter" + "maps" + "math/bits" + "slices" + "unsafe" + + "github.com/microsoft/TypeScript/tsc/internal/ast" + + "github.com/microsoft/TypeScript/tsc/internal/debug" +) + +// Checker side effects and memoization +// +// Checking a file produces outputs other than types: diagnostics, reference +// marks used by unused-identifier checks, deferred nodes, and so on. Those +// outputs are produced while computing memoized results (the type of a symbol, +// the resolved symbol of an identifier, ...). If such a result is first computed +// for an API query rather than while checking, a later check reuses the cached +// result without recomputing it, so the outputs would historically either leak +// (reported for code that checking never visits) or be lost (never reported for +// code that checking does visit). Both make diagnostics depend on query history. +// +// To make checking independent of query history, a checker is either checking +// or answering queries. A query does not apply side effects. Instead it +// records them against the innermost memoized computation in progress, along +// with the memoized results that computation reused. When a check later +// reuses a memoized result, it replays the recorded effects of that result +// and everything it transitively reused, exactly as if it had computed the +// result itself. Effects produced outside any memoized computation belong +// solely to the query and are discarded. A check applies its own effects as +// they happen, except while results it computes may be discarded, when it +// records them as a query does (see recordFromCheck). + +type memoField uint8 + +const ( + memoCell memoField = iota // A memo[T], keyed by its own address + memoWriteType + memoDeclaredType + memoTypeNodeType + memoBaseTypes + memoStructuredMembers + memoTypeParameterConstraint + memoTypeParameterDefault + memoEnumMemberValue + memoBaseConstraint + memoAccessorType + memoContextualParameters + memoContextChecked + memoInitializerIsUndefined + memoFlowExpressionType + memoFlowLoopType + memoJsxImplicitImportContainer + memoModuleExports + memoInstantiationExpressionType + memoTypePredicate + memoDeclaredMembers + memoCouldContainTypeVariables + memoGenericObjectFlags + memoUnknownLikeUnion + memoNeverIntersection + memoDiscriminant + memoResolvedMembers // followed by one field per MembersOrExportsResolutionKind + memoSymbolExports = memoResolvedMembers + memoField(MembersOrExportsResolutionKindResolvedExports) + memoSymbolMembers = memoResolvedMembers + memoField(MembersOrExportsResolutionKindResolvedMembers) + memoFieldCount = memoResolvedMembers + 2 +) + +// A reentry says what it means to re-enter a computation in progress, which +// can only observe whatever the computation has published so far. +type reentry uint8 + +const ( + // What re-entry observes is incomplete, so the results computed while + // the outer computation is in progress are discarded when it finishes, + // and computed again from its result. This is the default. + reentryCircular reentry = iota + // The computation detects re-entry with a type resolution of its own, + // which decides the head of the cycle and what to discard. + reentryResolution + // The computation publishes a partial result before it finishes, and + // re-entry observes that partial result. Results derived from it may + // differ depending on which computation starts first. Like re-entering + // a circular computation, it reads an incomplete result; see + // markReadIncomplete. + reentryPartial +) + +// memoReentry is the re-entry policy of each kind of computation. +var memoReentry = [memoFieldCount]reentry{ + // memoCompute keeps the result that a re-entrant computation cached, + // and many cells publish an in-progress marker for re-entry to read. + memoCell: reentryPartial, + // Many kinds of types make members available before they are complete. + memoStructuredMembers: reentryPartial, + // Circular defaults are reported at the default first in program order. + memoTypeParameterDefault: reentryResolution, + // Contextual parameter typing resolves a cycle through the enclosing call. + // Its type resolution ends once the parameters are assigned. + memoContextualParameters: reentryResolution, +} + +// memoKey identifies a memoized result: the address of the entity owning the +// cache slot (an *ast.Node, *ast.Symbol, *Type, or *Signature) shifted left by +// eight bits, plus the memoField of the slot. Packing into an integer keeps map +// operations on the fast path. Entities live as long as the program or the +// checker store, so an address cannot be reused while its keys exist. +type memoKey uint64 + +func (k memoKey) field() memoField { + return memoField(k) +} + +func memoKeyOf[T any](entity *T, field memoField) memoKey { + // SAFETY: Only the address is kept, as an identity; it is never turned back + // into a pointer. + return memoKey(uint64(uintptr(unsafe.Pointer(entity)))<<8 | uint64(field)) +} + +type memoEffects struct { + effects []func(c *Checker) + deps []*memoEffects + parent *memoEffects // Resolution d was most recently added to; avoids duplicate deps + entry *ast.Node // Current node when the computation started, if entered + rare *memoEffectsRare + entered bool // Whether this is the resolution of a computation, not just a set of deps + // Whether the result was discarded at the end of a cycle, so that its + // own effects are not replayed; see effectsToDrop. + dropped bool +} + +// memoEffectsRare holds the parts of memoEffects that few computations have, +// kept apart since the effects of every computation in a program can be +// retained at once. +type memoEffectsRare struct { + cycles []memoCycleDep // The cycle heads memoCycle added to deps, from highest to lowest +} + +func (f *memoEffects) rareFields() *memoEffectsRare { + if f.rare == nil { + f.rare = &memoEffectsRare{} + } + return f.rare +} + +func (f *memoEffects) addDep(d *memoEffects) { + if f == d || d.parent == f { + return + } + d.parent = f + f.deps = append(f.deps, d) +} + +// covers reports whether replaying f replays deps, checking only the direct +// dependencies of f, and only of a few, to stay cheap. +func (f *memoEffects) covers(deps []*memoEffects) bool { + if len(deps) > 16 || len(f.deps) > 64 { + return false + } + for _, d := range deps { + if d != f && !slices.Contains(f.deps, d) { + return false + } + } + return true +} + +// memoState is the memo state of one memoized result. +type memoState struct { + pending *memoEffects // Effects not yet replayed by a check + errors *[]*currentNodeError // Errors the computation reported at the current node, out of line since few have any; see setErrors +} + +// memo is a cached result. Cached checker state must live in a memo so that +// every read accounts for the effects of the computation that produced it; +// see the package comment above. The value is only accessible through +// methods. The memo state of a result is kept in the store's memo state map, +// keyed by the address of the memo, so a memo costs nothing beyond its value. +type memo[T any] struct { + memoHeader + memoValue T +} + +// memoHeader is the part of a memo independent of its type, which identifies +// it. +type memoHeader struct { + _ memoNoCopy +} + +// memoNoCopy makes go vet reject copies of a memo, which would bypass it. +type memoNoCopy struct{} + +func (*memoNoCopy) Lock() {} +func (*memoNoCopy) Unlock() {} + +func (h *memoHeader) key() memoKey { + return memoKeyOf(h, memoCell) +} + +// get returns the cached result, accounting for the effects of computing it. +func (m *memo[T]) get(c *Checker) T { + if c.observing() { + c.observeCell(&m.memoHeader) + } + return m.memoValue +} + +// set caches a result. When set outside the result's own computation (for +// example, the types of binding elements assigned while resolving the type of +// their declaration), the result depends on the computation setting it. +func (m *memo[T]) set(c *Checker, v T) { + if c.logResults() { + m.logDiscard(c) + } + m.memoValue = v + if !c.checking { + c.attachCell(&m.memoHeader) + } +} + +// setAfterMarker is set for a result replacing the marker its computation put +// in place when it began. A marker put in place before a cycle began was not +// logged, so discarding the result would restore it; a restored marker that +// stale reports as no longer in progress is cleared instead. Stale is given x +// rather than capturing it so that nothing is allocated unless the set is +// logged. +func setAfterMarker[T comparable, X any](c *Checker, m *memo[T], v T, x X, stale func(*Checker, X, T) bool) { + n := len(c.discardLog) + m.set(c, v) + if len(c.discardLog) > n { + restore := c.discardLog[n].restore + c.discardLog[n].restore = func() { + restore() + if stale(c, x, m.memoValue) { + var zero T + m.memoValue = zero + } + } + } +} + +// setIfChanged is set for a result that every computation needing it sets +// again, such as the symbol a property access resolves to. Setting the value +// it already has comes from the same computation, whose effects are already +// retained, so it is only set when it changes. +func setIfChanged[T comparable](c *Checker, m *memo[T], v T) { + if m.memoValue != v { + m.set(c, v) + } +} + +// setIdentity is set for a result that defines the identity of what it +// caches, such as a declared type, or that is part of an entity being +// created, such as the type of a synthetic property. It is never discarded at +// the end of a cycle: discarding an identity would let a second, distinct +// result exist, and an entity can outlive the cycle in caches that are kept +// while having no way to compute its parts again. +func (m *memo[T]) setIdentity(c *Checker, v T) { + m.memoValue = v + if !c.checking { + c.attachCell(&m.memoHeader) + } +} + +// A memoReported is a memo whose result can have errors to replay; see +// setReported. Reading it accounts for them even when nothing else must be +// accounted for. +type memoReported[T comparable] struct { + memo[T] +} + +func (m *memoReported[T]) get(c *Checker) T { + if c.observing() || c.memoErrorStates != 0 { + c.observeCell(&m.memoHeader) + } + return m.memoValue +} + +// setReported is setIdentity for a result whose computation collected errors +// reported at the current node, as returned by endCurrentNodeErrors. Every +// node reusing the result reports them again, as it would had it computed the +// result itself. If computing the result elsewhere may not report them (see +// endCurrentNodeErrors), it is instead discarded with other results cached +// for the current node. +func (m *memoReported[T]) setReported(c *Checker, v T, r currentNodeErrors) { + if r.cyclic { + if c.logResults() { + m.logDiscard(c) + } + r.list = nil + } + m.setIdentity(c, v) + if r.list != nil || c.memoErrorStates != 0 && c.peekMemoState(m.key()) != nil { + if r.list != nil { + i := errorFilterIndex(m.key()) + c.errorFilter[i/64] |= 1 << (i % 64) + } + c.memoState(m.key()).setErrors(c, r.list) + } +} + +//go:noinline +func (m *memo[T]) logDiscard(c *Checker) { + old := m.memoValue + c.logMemoDiscard(&m.memoHeader, m.key(), func() { + m.memoValue = old + }) +} + +// peek returns the cached result without accounting for effects. It is only +// for a result whose computation is in progress, where reading it does not +// observe it. +func (m *memo[T]) peek() T { + return m.memoValue +} + +// swap replaces the cached result without accounting for effects. It is +// only for speculative computations that restore the previous result after. +func (m *memo[T]) swap(v T) T { + old := m.memoValue + m.memoValue = v + return old +} + +// intern sets the result of a cell whose owner is being created. Nothing can +// observe the owner before its creator returns it, so the result needs no +// dependency of its own. +func (m *memo[T]) intern(v T) { + m.memoValue = v +} + +// begin starts the computation of the result; see beginMemo. +func (m *memo[T]) begin(c *Checker) { + c.beginMemo(m.key()) +} + +// memoCompute returns the result cached in m, computing it in a computation +// of its own if there is none. A result cached while computing it, by a +// computation that re-entered it, is kept. +func (c *Checker) memoCompute[T comparable](m *memo[T], compute func() T) T { + var zero T + if v := m.get(c); v != zero { + return v + } + m.begin(c) + v := compute() + if cur := m.peek(); cur != zero { + v = cur + } else { + m.set(c, v) + } + c.endMemo() + return v +} + +// internMap is a map of results each constructed from the inputs in its key +// alone, without consulting any cached result, so caching or reusing one has +// no effects to account for. +type internMap[K comparable, V any] struct { + _ memoNoCopy + entries map[K]V +} + +func (m *internMap[K, V]) get(k K) (V, bool) { + v, ok := m.entries[k] + return v, ok +} + +func (m *internMap[K, V]) has(k K) bool { + _, ok := m.entries[k] + return ok +} + +func (m *internMap[K, V]) intern(k K, v V) { + if m.entries == nil { + m.entries = make(map[K]V) + } + m.entries[k] = v +} + +func (m *internMap[K, V]) isEmpty() bool { + return len(m.entries) == 0 +} + +func (m *internMap[K, V]) values() iter.Seq[V] { + return maps.Values(m.entries) +} + +// memoMap is a map of cached results, each accounted for like a memo. The +// entries are only accessible through methods. Memo state is kept per entry +// in a side map allocated on first use by a query. +// +// Most maps never need anything but their entries, and many are embedded in +// types, so everything else lives in a side table allocated on first use. +// +// An entry should be computed between enter and endMemo, so that reusing it +// replays the effects of computing that entry alone. An entry set outside its +// own computation depends on the whole enclosing computation instead. +type memoMap[K comparable, V any] struct { + _ memoNoCopy + memoEntries map[K]V + side *memoMapSide[K] + // The number of writes to memoEntries, with which a caller can tell + // that an entry it read is still current without reading it again. + writes uint32 +} + +type memoMapSide[K comparable] struct { + memoStates map[K]*memoState + resolvingKeys []K // Keys of computations in progress without a memo state yet + resolvingAt []int32 // Index in Checker.resolutions of each + // The occurrences of each key in resolvingKeys, kept while there are + // many, so that looking one up does not scan them all. + resolvingCount map[K]resolvingOccurrences + // If set, keyBits returns well-mixed bits of a key, with which stateKeys + // filters the keys that may have a memo state, since few do and looking + // up one that does not costs as much as one that does. + keyBits func(K) uint64 + stateKeys stateKeyFilter + hasErrors bool // Whether an entry ever had reports; see setReported +} + +func (m *memoMap[K, V]) sideTable() *memoMapSide[K] { + if m.side == nil { + m.side = &memoMapSide[K]{} + } + return m.side +} + +// setKeyBits makes the map filter the keys that may have a memo state with +// keyBits; see memoMapSide. +func (m *memoMap[K, V]) setKeyBits(keyBits func(K) uint64) { + m.sideTable().keyBits = keyBits +} + +// states returns the memo states of the entries, or nil. +func (m *memoMap[K, V]) states() map[K]*memoState { + if m.side == nil { + return nil + } + return m.side.memoStates +} + +// stateOf returns the memo state of the entry for k, or nil. +func (m *memoMap[K, V]) stateOf(k K) *memoState { + s := m.side + if s == nil || s.memoStates == nil { + return nil + } + if s.keyBits != nil && !s.stateKeys.mayContain(s.keyBits(k)) { + return nil + } + return s.memoStates[k] +} + +// get returns the cached result for k, accounting for the effects of +// computing it. +func (m *memoMap[K, V]) get(c *Checker, k K) (V, bool) { + if m.side != nil && (c.observing() || m.side.hasErrors) { + m.observe(c, k) + } + v, ok := m.memoEntries[k] + return v, ok +} + +// observe is the out of line part of get, keeping get cheap enough to inline. +// +//go:noinline +func (m *memoMap[K, V]) observe(c *Checker, k K) { + if m.side.memoStates == nil && len(m.side.resolvingKeys) == 0 { + return + } + if c.checking && c.memoPendingCount == 0 && !m.side.hasErrors { + // Only reports are replayed; see observeMemo. + return + } + // A computation in progress may not have a memo state yet; see + // resolutionState. + if state := m.stateOf(k); c.observeState(state) || state == nil && !c.checking { + c.memoCycle(m.resolvingIndex(c, k)) + } +} + +// has reports whether a result is cached for k; see get. +func (m *memoMap[K, V]) has(c *Checker, k K) bool { + _, ok := m.get(c, k) + return ok +} + +// isEmpty reports whether no result is cached, without observing any. +func (m *memoMap[K, V]) isEmpty() bool { + return len(m.memoEntries) == 0 +} + +// values iterates the cached results without observing them, for +// inspection outside of checking. +func (m *memoMap[K, V]) values() iter.Seq[V] { + return maps.Values(m.memoEntries) +} + +// set caches the result for k; see memo.set. +func (m *memoMap[K, V]) set(c *Checker, k K, v V) { + if c.logResults() { + m.logDiscard(c, k) + } + m.setIdentity(c, k, v) +} + +//go:noinline +func (m *memoMap[K, V]) logDiscard(c *Checker, k K) { + old, ok := m.memoEntries[k] + c.logResultDiscard(c.memoTop != 0 && m.isOwnResolution(c, k), nil, func() { + m.writes++ + if ok { + m.memoEntries[k] = old + } else { + delete(m.memoEntries, k) + } + }) +} + +// setReported is setIdentity for an entry; see memo.setReported. +func (m *memoMap[K, V]) setReported(c *Checker, k K, v V, r currentNodeErrors) { + if r.cyclic { + if c.logResults() { + m.logDiscard(c, k) + } + r.list = nil + } + m.setIdentity(c, k, v) + if r.list != nil || c.memoErrorStates != 0 && m.stateOf(k) != nil { + if r.list != nil { + m.sideTable().hasErrors = true + } + m.state(k).setErrors(c, r.list) + } +} + +// setIdentity is set for a result that defines the identity of what it +// caches; see memo.setIdentity. +func (m *memoMap[K, V]) setIdentity(c *Checker, k K, v V) { + if m.memoEntries == nil { + m.memoEntries = make(map[K]V) + } + m.memoEntries[k] = v + m.writes++ + m.attach(c, k) +} + +// attach is memoAttach for the entry for k. +func (m *memoMap[K, V]) attach(c *Checker, k K) { + if c.memoTop == 0 || c.checking { + return + } + if f := &c.resolutions[c.memoTop-1]; f.source == memoStateSource(m) && m.side.resolvingKeys[f.index] == k { + return + } + c.attachState(m.state(k)) +} + +// intern caches a result constructed from the inputs in k alone, without +// consulting any cached result, so computing it has no effects to account for. +func (m *memoMap[K, V]) intern(k K, v V) { + if m.memoEntries == nil { + m.memoEntries = make(map[K]V) + } + m.memoEntries[k] = v + m.writes++ +} + +// compute computes and caches the result for k in a computation of its own. +func (m *memoMap[K, V]) compute(c *Checker, k K, fn func() V) V { + m.begin(c, k) + v := fn() + m.set(c, k, v) + c.endMemo() + return v +} + +// begin starts the computation of the result for k; see beginMemo. +func (m *memoMap[K, V]) begin(c *Checker, k K) { + side := m.sideTable() + outer := 0 + if len(side.resolvingKeys) != 0 { + outer = m.resolvingIndex(c, k) + } + if outer != 0 { + // Re-entering an entry marks the cycle circular; see reentryCircular. + c.markCircular(outer - 1) + c.markReadIncomplete(outer - 1) + } + side.resolvingKeys = append(side.resolvingKeys, k) + side.resolvingAt = append(side.resolvingAt, int32(len(c.resolutions))) + if side.resolvingCount != nil { + side.countResolving(k, len(side.resolvingKeys)-1) + } else if len(side.resolvingKeys) > resolvingCountMin { + side.countAllResolving() + } + c.beginResolution(m, len(side.resolvingKeys)-1) + if c.checking && c.memoPendingCount == 0 && c.memoTop|c.currentNodeResolution != 0 && !c.logEffects() { + // beginCheckedMemo for the common case, without the calls. The memo + // state of the result is looked up only if needed; see + // resolutionState. + n := len(c.resolutions) - 1 + f := &c.resolutions[n] + f.memo = true + f.prevMemo = int32(c.memoTop) + f.entry = c.currentNode + f.attachStart = int32(len(c.deferredAttach)) + c.memoTop = n + 1 + return + } + // Looked up after marking a cycle, which may give the entry a state. + state := m.stateOf(k) + if c.checking { + if c.memoPendingCount != 0 && state != nil { + c.replayPending(state) + } + c.beginCheckedMemo(state) + return + } + c.beginState(state, outer) +} + +// resolvingIndex returns 1 + the index in resolutions of the computation of +// the entry for k in progress, or 0. +func (m *memoMap[K, V]) resolvingIndex(c *Checker, k K) int { + if m.side == nil { + return 0 + } + start := 0 + if m.side.resolvingCount != nil { + o, ok := m.side.resolvingCount[k] + if !ok { + return 0 + } + start = int(o.first) + if o.count == 1 { + if j := int(m.side.resolvingAt[start]); j >= c.visibleStart { + return j + 1 + } + return 0 + } + } + for i := start; i < len(m.side.resolvingKeys); i++ { + if m.side.resolvingKeys[i] != k { + continue + } + j := int(m.side.resolvingAt[i]) + debug.Assert(c.resolutions[j].source == memoStateSource(m) && int(c.resolutions[j].index) == i, "entry in progress without a resolution") + if j >= c.visibleStart { + return j + 1 + } + // Hidden; see yieldCycle. + } + return 0 +} + +// isOwnResolution reports whether the current resolution computes the entry +// for k. +func (m *memoMap[K, V]) isOwnResolution(c *Checker, k K) bool { + f := c.currentResolution() + return f.source == memoStateSource(m) && m.side.resolvingKeys[f.index] == k +} + +func (m *memoMap[K, V]) stateAt(i int) *memoState { + return m.state(m.side.resolvingKeys[i]) +} + +func (m *memoMap[K, V]) peekStateAt(i int) *memoState { + return m.stateOf(m.side.resolvingKeys[i]) +} + +func (m *memoMap[K, V]) popState(i int) { + if counts := m.side.resolvingCount; counts != nil { + if i < resolvingCountMin/4 { + m.side.resolvingCount = nil + } else { + for _, k := range m.side.resolvingKeys[i:] { + // Keys are popped last in first out, so the first + // occurrence is popped last. + if o := counts[k]; o.count == 1 { + delete(counts, k) + } else { + o.count-- + counts[k] = o + } + } + } + } + clear(m.side.resolvingKeys[i:]) + m.side.resolvingKeys = m.side.resolvingKeys[:i] + m.side.resolvingAt = m.side.resolvingAt[:i] +} + +// Number of computations of a memo map in progress above which they are +// counted by key; see resolvingCount. +const resolvingCountMin = 64 + +type resolvingOccurrences struct { + first int32 // Index in resolvingKeys of the first + count int32 +} + +func (side *memoMapSide[K]) countAllResolving() { + side.resolvingCount = make(map[K]resolvingOccurrences, 2*len(side.resolvingKeys)) + for i, k := range side.resolvingKeys { + side.countResolving(k, i) + } +} + +func (side *memoMapSide[K]) countResolving(k K, i int) { + o, ok := side.resolvingCount[k] + if !ok { + o.first = int32(i) + } + o.count++ + side.resolvingCount[k] = o +} + +func (m *memoMap[K, V]) state(k K) *memoState { + state := m.stateOf(k) + if state == nil { + s := m.sideTable() + if s.memoStates == nil { + s.memoStates = make(map[K]*memoState) + } + state = &memoState{} + s.memoStates[k] = state + if s.keyBits != nil { + s.stateKeys.add(s.keyBits(k), s.memoStates, s.keyBits) + } + } + return state +} + +// memoSet tracks computations that run at most once per key, such as checks +// whose only results are diagnostics. Each run is a computation of its own, so +// skipping a key that already ran accounts for the effects of that run. +type memoSet[K comparable] struct { + m memoMap[K, struct{}] +} + +// has reports whether k has run; see memoMap.get. +func (s *memoSet[K]) has(c *Checker, k K) bool { + return s.m.has(c, k) +} + +// once runs fn unless k has run already. k is marked before fn runs, so fn +// does not run again if it re-enters. +func (s *memoSet[K]) once(c *Checker, k K, fn func()) { + if s.m.has(c, k) { + return + } + s.m.begin(c, k) + s.m.set(c, k, struct{}{}) + fn() + c.endMemo() +} + +// memoActive reports whether reading a cached result must account for its +// effects: false in a check with nothing pending. +func (c *Checker) memoActive() bool { + return !c.checking || c.memoPendingCount|c.memoErrorStates != 0 +} + +// observing reports whether reading a cached result that cannot have errors +// to replay must account for its effects: memoActive, except for results +// with errors, which only memoReported and memoMap cache. It is kept in a +// field for the many reads that check it. +func (c *Checker) observing() bool { + return c.observingMemos +} + +// syncObserving updates observingMemos after a change to what it depends +// on: the checking flag or the counts it reads. The counts are shared with other checkers using the same +// store, but those only run while this one is not checking, and it syncs on +// starting to check. +func (c *Checker) syncObserving() { + c.observingMemos = !c.checking || c.memoPendingCount != 0 +} + +// readsWithoutMemo reports whether a memoized computation identified by key, +// whose result is cached, can read it without beginning the memo: when no +// memo is active, the memo records and replays nothing, and the computation +// only reads, unless it re-enters itself in progress, with the result +// published before it is complete. +func (c *Checker) readsWithoutMemo(key memoKey) bool { + if c.observing() { + return false + } + _, resolving := c.resolving.get(key) + return !resolving +} + +// beginMemo is called on entry to a memoized function whose result for this +// input is cached in the memo state identified by key. It accounts for +// effects recorded by a previous computation of that result, and starts a +// resolution for this computation, which in a query records its effects. It +// must be matched by endMemo. +func (c *Checker) beginMemo(key memoKey) { + resolving := c.beginResolving(key) + c.appendResolution(key, nil, 0, int32(len(c.instantiationStack))) + n := len(c.resolutions) - 1 + f := &c.resolutions[n] + f.resolving = resolving + if c.checking && c.memoPendingCount == 0 && c.memoTop|c.currentNodeResolution != 0 && !c.logEffects() { + // beginCheckedMemo for the common case, without the calls. The memo + // state of the result is looked up only if needed; see + // resolutionState. + f.memo = true + f.prevMemo = int32(c.memoTop) + f.entry = c.currentNode + f.attachStart = int32(len(c.deferredAttach)) + c.memoTop = n + 1 + return + } + state := c.peekMemoState(key) + if c.checking { + if c.memoPendingCount != 0 { + c.replayPending(state) + } + c.beginCheckedMemo(state) + return + } + outer := c.resolvingIndex(key) + if outer == len(c.resolutions) { + outer = 0 + } + c.beginState(state, outer) +} + +// beginState is beginMemo for the memo state of a result, in a query. outer +// is 1 + the index in resolutions of the computation of the same result in +// progress, if this one re-enters it, or 0. +func (c *Checker) beginState(state *memoState, outer int) { + if state != nil && state.pending != nil { + c.observePending(state.pending) + } + c.startEffects(state) + // Re-entering a computation in progress: the inner result is derived + // from whatever in-progress marker the outer computation left behind. + c.memoCycle(outer) +} + +// startEffects starts recording the effects of the current resolution, which +// computes the result in state. +func (c *Checker) startEffects(state *memoState) { + n := len(c.resolutions) - 1 + f := &c.resolutions[n] + f.memo = true + f.prevMemo = int32(c.memoTop) + f.result = state + f.entry = c.currentNode + f.attachStart = int32(len(c.deferredAttach)) + if c.memoTop == 0 && c.currentNodeResolution == 0 { + // A query made outside of checking: errors located at the current + // node are located wherever checking reuses the result, so results + // they depend on are discarded once the query finishes (see + // reportAtCurrentNode), and every reuse computes them again. The + // node's resolution is entered above the computation's resolution. + debug.Assert(c.queryRoot == 0, "nested query") + c.queryRoot = n + 1 + c.querySave = c.enterCurrentNode(c.currentNode) + } + c.memoTop = n + 1 +} + +// stopEffects stops recording the effects of the current resolution, +// returning it if it recorded them. +func (c *Checker) stopEffects() *resolution { + n := len(c.resolutions) - 1 + if c.queryRoot == n && c.currentNodeResolution == n+1 { + n-- + c.exitCurrentNode(c.querySave) + c.queryRoot = 0 + } + f := &c.resolutions[n] + if !f.memo { + return nil + } + c.memoTop = int(f.prevMemo) + return f +} + +func (c *Checker) memoState(key memoKey) *memoState { + state := c.memoStates[key] + if state == nil { + if c.memoStates == nil { + c.memoStates = make(map[memoKey]*memoState) + } + state = &memoState{} + c.memoStates[key] = state + c.stateFilter.add(memoKeyBits(key), c.memoStates, memoKeyBits) + } + return state +} + +// stateKeyFilter is a filter of the keys of a map of memo states, given by +// well-mixed bits of each key, sized to the number of states so that it stays +// in cache: few results have a memo state. +type stateKeyFilter struct { + words []uint64 + shift uint8 // 64 - log2 of the number of bits +} + +// Bits per state below which the filter grows, keeping false positives +// to a few percent. +const stateFilterBitsPerState = 16 + +func memoKeyBits(key memoKey) uint64 { + return uint64(key) * 0x9E3779B97F4A7C15 +} + +func (f *stateKeyFilter) mayContain(h uint64) bool { + if f.words == nil { + return false + } + i := h >> f.shift + return f.words[i/64]&(1<<(i%64)) != 0 +} + +// add adds the key with bits h, which states has just been given a state +// for. +func (f *stateKeyFilter) add[K comparable](h uint64, states map[K]*memoState, keyBits func(K) uint64) { + if n := 64 * len(f.words); n < stateFilterBitsPerState*len(states) { + n = max(2*n, 1<<15) + f.words = make([]uint64, n/64) + f.shift = uint8(65 - bits.Len(uint(n))) + for k := range states { + f.set(keyBits(k)) + } + return + } + f.set(h) +} + +func (f *stateKeyFilter) set(h uint64) { + i := h >> f.shift + f.words[i/64] |= 1 << (i % 64) +} + +const errorFilterBits = 12 + +type errorKeyFilter [1 << errorFilterBits / 64]uint64 + +func errorFilterIndex(key memoKey) uint64 { + return uint64(key) * 0x9E3779B97F4A7C15 >> (64 - errorFilterBits) +} + +// peekMemoState returns the memo state of the result identified by key, or +// nil if it has none, consulting a filter first since most results have none. +func (c *Checker) peekMemoState(key memoKey) *memoState { + if !c.stateFilter.mayContain(memoKeyBits(key)) { + return nil + } + return c.memoStates[key] +} + +// memoAttach makes the result identified by key depend on the innermost +// computation in progress, unless that computation is the result's own. +func (c *Checker) memoAttach(key memoKey) { + if c.memoTop == 0 || c.checking { + return + } + if f := &c.resolutions[c.memoTop-1]; f.source == nil && f.key == key { + return + } + if c.resolutions[c.memoTop-1].effects == nil && !c.logEffects() { + // See attachState; the memo state is created only if needed. + c.deferredAttach = append(c.deferredAttach, deferredAttachment{key: key}) + return + } + c.attachState(c.memoState(key)) +} + +// A deferredAttachment is a result cached by a computation that had no +// effects yet; see attachState. A result of a memo cell is identified by its +// key, so that its memo state is created only if needed. +type deferredAttachment struct { + state *memoState + key memoKey +} + +// attachState is memoAttach for the memo state of a result. +func (c *Checker) attachState(state *memoState) { + n := c.memoTop - 1 + if n < 0 { + return + } + if c.peekResolutionState(&c.resolutions[n]) == state { + // Writers skip their own computation before creating its state, so + // a state created later is never the computation's own. + return + } + if c.resolutions[n].effects == nil && !c.logEffects() { + // Until the computation has effects, the result depends on nothing, + // and most computations never have any; memoEffectsAt attaches the + // result if it gets some. + c.deferredAttach = append(c.deferredAttach, deferredAttachment{state: state}) + return + } + c.linkState(state, c.memoEffectsAt(n), c.logResults()) +} + +// linkState makes the result in state depend on the effects f of the +// computation that cached it, logging the change in the discard log if log. +func (c *Checker) linkState(state *memoState, f *memoEffects, log bool) { + if log { + c.logPendingDiscard(state) + } + switch { + case state.pending == nil: + // Replaying f is replaying a set of deps holding only f. + state.pending = f + c.memoPendingCount++ + c.syncObserving() + if c.recordingFromCheck { + c.flushBuf = append(c.flushBuf, state) + } + case state.pending == f: + // Already depends on f. + case state.pending.entered: + // The effects of a computation are shared with whatever else + // depends on it, so they cannot gain a dependency of the result. + deps := &memoEffects{} + deps.addDep(state.pending) + deps.addDep(f) + state.pending = deps + default: + state.pending.addDep(f) + } +} + +// observeCell and attachCell are observeMemo and memoAttach for a memo at +// address p. They are kept out of line so that memo reads and writes are +// cheap enough to inline. +// +//go:noinline +func (c *Checker) observeCell(h *memoHeader) { + c.observeMemo(h.key()) +} + +//go:noinline +func (c *Checker) attachCell(h *memoHeader) { + c.memoAttach(h.key()) +} + +// observeMemo accounts for reading a memoized result without computing it. +func (c *Checker) observeMemo(key memoKey) { + if !c.memoActive() { + return + } + if c.checking && c.memoPendingCount == 0 { + // Only reports are replayed, which few results have, so a filter + // small enough to stay in cache rules out most. + if i := errorFilterIndex(key); c.errorFilter[i/64]&(1<<(i%64)) == 0 { + return + } + } + // A computation in progress may not have a memo state yet; see + // resolutionState. + if state := c.peekMemoState(key); c.observeState(state) || state == nil && !c.checking && c.resolving.count != 0 { + c.memoCycle(c.resolvingIndex(key)) + } +} + +// observeState is observeMemo for the memo state of a result, if it has one. +// It reports whether the result is read in a query, in which case reading a +// computation in progress is left to the caller; see memoCycle. +func (c *Checker) observeState(state *memoState) bool { + if state == nil { + return false + } + if state.errors != nil { + c.replayCurrentNodeErrors(*state.errors) + } + if c.checking { + c.replayPending(state) + return false + } + if state.pending != nil { + c.observePending(state.pending) + } + return true +} + +// observePending adds the effects of a reused result to the enclosing +// computation. Those of a computation started at no node are located where +// it is reused, so if that is not where the enclosing computation started, +// the node is kept with them; replay would otherwise locate them wherever the +// enclosing computation is reused. +func (c *Checker) observePending(pending *memoEffects) { + parent := c.currentMemoEffects() + if parent == nil { + return + } + if c.currentNode != nil && c.currentNode != parent.entry && (!pending.entered || pending.entry == nil) { + pending = &memoEffects{deps: []*memoEffects{pending}, entry: c.currentNode, entered: true} + } + parent.addDep(pending) +} + +// A currentNodeError is an error reported at the current node; see +// reportAtCurrentNode. Replaying it reports the same *currentNodeError, so +// that reports collected more than once can be told apart from distinct ones. +type currentNodeError struct { + report func(c *Checker, node *ast.Node) +} + +type currentNodeErrorsMark struct { + errors int + cyclic bool +} + +// currentNodeErrors are the errors a computation reported at the current +// node. +type currentNodeErrors struct { + list []*currentNodeError + cyclic bool // Whether the computation observed a cycle, so computing it elsewhere may not report them +} + +// startCurrentNodeErrors starts collecting the errors a computation reports +// at the current node, to be passed to endCurrentNodeErrors when it finishes. +func (c *Checker) startCurrentNodeErrors() currentNodeErrorsMark { + c.currentNodeErrorCollectors++ + mark := currentNodeErrorsMark{len(c.currentNodeErrors), c.currentNodeErrorsCyclic} + c.currentNodeErrorsCyclic = false + return mark +} + +// endCurrentNodeErrors returns the errors reported at the current node since +// mark. +func (c *Checker) endCurrentNodeErrors(mark currentNodeErrorsMark) currentNodeErrors { + var r currentNodeErrors + for _, report := range c.currentNodeErrors[mark.errors:] { + // Reusing a result more than once reports its errors each time. + if !slices.Contains(r.list, report) { + r.list = append(r.list, report) + } + } + // Which errors a cycle reports depends on where it is entered, so they + // are only reported where the result is computed. + r.cyclic = r.list != nil && c.currentNodeErrorsCyclic + c.currentNodeErrorsCyclic = c.currentNodeErrorsCyclic || mark.cyclic + c.currentNodeErrorCollectors-- + if c.currentNodeErrorCollectors == 0 { + clear(c.currentNodeErrors) + c.currentNodeErrors = c.currentNodeErrors[:0] + c.currentNodeErrorsCyclic = false + } + return r +} + +func (s *memoState) setErrors(c *Checker, reports []*currentNodeError) { + switch { + case s.errors == nil && reports != nil: + c.memoErrorStates++ + c.syncObserving() + case s.errors != nil && reports == nil: + c.memoErrorStates-- + c.syncObserving() + } + if reports == nil { + s.errors = nil + } else { + s.errors = &reports + } +} + +// replayCurrentNodeErrors reports at the current node the errors that +// computing a reused result reported at the node it was computed for. +// +//go:noinline +func (c *Checker) replayCurrentNodeErrors(reports []*currentNodeError) { + for _, r := range reports { + c.reportNode(r) + } +} + +func (c *Checker) replayPending(state *memoState) { + if state != nil && state.pending != nil { + f := state.pending + state.pending = nil + c.memoPendingCount-- + c.syncObserving() + c.replayMemo(f) + } +} + +// endMemo finishes a computation started by beginMemo, retaining its effects +// for replay by later reuse of the result. The effects also become part of +// the enclosing computation, since it observed the result. +func (c *Checker) endMemo() { + n := len(c.resolutions) - 1 + if f := &c.resolutions[n]; f.memo && f.effects == nil && !f.fromCheck { + // stopEffects and endResolution for the common case, without the + // calls. + c.memoTop = int(f.prevMemo) + if f.resolving { + c.resolving.delete(f.key) + } + if int(f.attachStart) != len(c.deferredAttach) { + clear(c.deferredAttach[f.attachStart:]) + c.deferredAttach = c.deferredAttach[:f.attachStart] + } + if f.source != nil { + f.source.popState(int(f.index)) + } + c.dropResolution(n) + return + } + if f := c.stopEffects(); f != nil && f.effects != nil { + c.retainEffects(c.resolutionState(f), f.effects) + } + c.endResolution() +} + +// markReadIncomplete marks the computations in progress above the one at +// index i, which is re-entered, as reading its incomplete result: either the +// in-progress marker of a circular computation or the part of a partial +// result published so far. What they compute from here on may be derived +// from it; see logResultDiscard. +func (c *Checker) markReadIncomplete(i int) { + for j := i + 1; j < len(c.resolutions); j++ { + c.resolutions[j].readIncomplete = true + } +} + +// endMemoUncached is endMemo for a computation that did not cache its result. +// Nothing can reuse the result, so its effects belong only to the enclosing +// computation, and retaining them for the result would grow them each time it +// is computed again. +func (c *Checker) endMemoUncached() { + defer c.endResolution() + res := c.stopEffects() + c.forgetResult() + if res == nil || res.effects == nil { + return + } + f := res.effects + if parent := c.currentMemoEffects(); parent != nil { + if len(f.effects) == 0 && f.entry == parent.entry { + // Without effects of its own, and located like the enclosing + // computation, the computation adds nothing but what it observed. + for _, d := range f.deps { + parent.addDep(d) + } + } else { + parent.addDep(f) + } + } else if c.recordingFromCheck { + c.retainEffects(c.resolutionState(res), f) + } +} + +// resolutionState returns the memo state of the result a memo resolution +// computes, creating it on first use, since most computations have no +// effects to retain. +func (c *Checker) resolutionState(f *resolution) *memoState { + if f.result == nil { + switch { + case f.source != nil: + f.result = f.source.stateAt(int(f.index)) + case f.key != 0: + f.result = c.memoState(f.key) + default: + // The computation caches no result; see forgetResult. + f.result = &memoState{} + } + } + return f.result +} + +// peekResolutionState is resolutionState without creating the memo state, +// returning nil if the result has none yet. +func (c *Checker) peekResolutionState(f *resolution) *memoState { + if f.result == nil { + switch { + case f.source != nil: + f.result = f.source.peekStateAt(int(f.index)) + case f.key != 0: + f.result = c.peekMemoState(f.key) + } + } + return f.result +} + +// retainEffects retains the effects of a computation that just finished for +// replay by later reuse of its result, and adds them to the enclosing +// computation. +func (c *Checker) retainEffects(state *memoState, f *memoEffects) { + if state.pending != nil && len(f.effects) == 0 && state.pending.covers(f.deps) { + // Computing the result again, typically to return it from a cache + // that the computation reads, observed only what computing it + // before did. Retaining that again would grow the effects of the + // result each time. + if parent := c.currentMemoEffects(); parent != nil { + parent.addDep(state.pending) + } + return + } + if state.pending == nil && len(f.effects) == 0 && f.rare == nil && len(f.deps) == 1 && f.deps[0].entered && f.deps[0].entry == f.entry { + // The computation only observed one result computed at the same + // node, whose effects replay the same way without it. + f = f.deps[0] + } + if c.logEffects() { + c.logPendingDiscard(state) + } + if state.pending != nil { + f.addDep(state.pending) + } else { + c.memoPendingCount++ + c.syncObserving() + if c.recordingFromCheck { + c.flushBuf = append(c.flushBuf, state) + } + } + state.pending = f + if parent := c.currentMemoEffects(); parent != nil { + parent.addDep(f) + } +} + +func (c *Checker) replayMemo(f *memoEffects) { + c.replayEffects(f, nil, false) +} + +// replayEffects replays the effects of f and its dependencies. Effects +// located at the current node that a computation started with (see +// reportAtCurrentNode) are located at the node the reuse happens at instead, +// as they would be had the computation happened there, unless the computation +// started at a different node than the computation replaying it, which then +// fixes its location. Replaying f empties it, so that revisiting it through +// other dependencies replays nothing. +func (c *Checker) replayEffects(f *memoEffects, outerEntry *ast.Node, outerEntered bool) { + effects, deps := f.effects, f.deps + f.effects, f.deps, f.rare = nil, nil, nil + saveCurrentNode := c.currentNode + if f.entered { + // A computation that started at no node can be located anywhere, + // like one started at the node it is reused at. + if outerEntered && f.entry != outerEntry && f.entry != nil { + c.currentNode = f.entry + } + outerEntry, outerEntered = f.entry, true + } + if !f.dropped { + for _, e := range effects { + e(c) + } + } + for _, d := range deps { + c.replayEffects(d, outerEntry, outerEntered) + } + c.currentNode = saveCurrentNode +} + +// atMemoStartNode reports whether the current node is the one the innermost +// computation in progress started with. +func (c *Checker) atMemoStartNode() bool { + n := c.memoTop - 1 + return n >= 0 && c.resolutions[n].entry == c.currentNode +} + +func (c *Checker) currentMemoEffects() *memoEffects { + if c.memoTop == 0 { + return nil + } + return c.memoEffectsAt(c.memoTop - 1) +} + +// recordEffect records a side effect produced by a query against the +// innermost memoized computation, if any. Callers must only call this when +// !c.checking, and must not apply the effect themselves. +func (c *Checker) recordEffect(effect func(c *Checker)) { + if f := c.currentMemoEffects(); f != nil { + f.effects = append(f.effects, effect) + } +} + +// beginCheckedMemo is beginMemo for a computation started in a check. Its +// effects apply as they happen, and reading cached results accounts for +// nothing, as every effect only adds to what the store has collected, which +// is the same whichever check applies it first. Effects of results that a +// cycle or a discard mark may discard are the exception (see recordFromCheck), +// so a computation starting while one is in progress records them. +func (c *Checker) beginCheckedMemo(state *memoState) { + c.startEffects(state) + if c.logEffects() { + c.recordFromCheck() + } +} + +// recordFromCheck makes the outermost computation in progress in a check, if +// any, a query from here on, because a cycle or a discard mark begins, after which +// results and their effects may be discarded. Effects are then recorded with +// the results computed and with every result observing them, and are replayed +// when the outermost computation finishes (see endQueryFromCheck), so that +// those of discarded results apply only if a result that is kept observed +// them. The computations in progress applied their earlier effects already, +// which is right since they are kept: a cycle discards only what is computed +// after it is found, and a mark only what is computed after it is taken. +func (c *Checker) recordFromCheck() { + if !c.checking || c.memoTop == 0 { + return + } + debug.Assert(!c.recordingFromCheck, "recording from a check already recording") + i := c.memoTop - 1 + for c.resolutions[i].prevMemo != 0 { + i = int(c.resolutions[i].prevMemo) - 1 + } + c.resolutions[i].fromCheck = true + c.checking = false + c.syncObserving() + c.flushBuf = c.flushBuf[:0] + c.recordingFromCheck = true +} + +// endQueryFromCheck ends the query begun by recordFromCheck, replaying +// the effects of everything it computed in the check. +func (c *Checker) endQueryFromCheck() { + if !c.recordingFromCheck { + // The query already ended; see resumeChecking. + return + } + states := c.flushBuf + c.recordingFromCheck = false + c.checking = true + c.syncObserving() + defer clear(states) + c.replayFlushed(states) +} + +func (c *Checker) replayFlushed(states []*memoState) { + for _, state := range states { + if state.pending != nil { + f := state.pending + state.pending = nil + c.memoPendingCount-- + c.syncObserving() + c.replayEffects(f, c.currentNode, true) + } + } +} + +// resumeChecking ends the query begun by recordFromCheck as soon as nothing +// computed since can be discarded: no cycle is in progress and no effects +// mark is held. Recording until the outermost computation finishes would be +// equivalent, but makes every read until then account for effects. What the +// query recorded applies now, as it would have in the check, including what +// the computations in progress recorded, which continue as in a check. +func (c *Checker) resumeChecking() { + if c.checking || !c.recordingFromCheck || c.logEffects() || len(c.innerCycles) != 0 || len(c.discardLog) != 0 { + return + } + chain := c.resumeBuf[:0] + for i := c.memoTop - 1; i >= 0; i = int(c.resolutions[i].prevMemo) - 1 { + chain = append(chain, i) + } + saveCurrentNode := c.currentNode + if len(chain) != 0 { + root := &c.resolutions[chain[len(chain)-1]] + root.fromCheck = false + if root.entry != nil { + // The effects apply as they would when the outermost computation + // finishes, back at the node it started at. + c.currentNode = root.entry + } + } + states := c.flushBuf + c.recordingFromCheck = false + c.checking = true + c.syncObserving() + c.replayFlushed(states) + clear(states) + loc := c.currentNode + for _, i := range slices.Backward(chain) { + r := &c.resolutions[i] + if f := r.effects; f != nil { + r.effects = nil + c.currentNode = loc + c.replayEffects(f, loc, true) + } + if r.entry != nil { + loc = r.entry + } + } + c.currentNode = saveCurrentNode + clear(chain) + c.resumeBuf = chain[:0] +} + +// withChecking runs fn in a check. +func (c *Checker) withChecking(fn func()) { + // Checking starts only from top-level requests, so any resolution left at + // this point was never exited and would absorb unrelated effects. + debug.Assert(c.memoTop == 0, "memo resolution leaked") + saveChecking := c.checking + c.checking = true + c.syncObserving() + fn() + c.checking = saveChecking + c.syncObserving() +} + +// memoCycle is called when a computation observes another computation that is +// still in progress, whose resolution is at index depth-1. Every computation +// started since then produced its result from the incomplete state of that +// computation, so reusing any of them must also replay the effects of the +// whole cycle (for example, its circularity error). +func (c *Checker) memoCycle(depth int) { + if depth == 0 || !c.resolutions[depth-1].memo { + // The outer computation does not record effects. + return + } + var outer *memoEffects + // A computation that depends on a cycle head depends on every computation + // in progress above the head, as their effects become part of the head's. + // So walk down from the top, stopping at a computation an earlier reentry + // already made depend on outer or a head below it: the computations below + // it were in progress then too, so they depend on that head as well. + // Without this, every reentry of a cycle would walk, and grow, the whole + // stack. + head := depth - 1 + for i := len(c.resolutions) - 1; i >= depth; i-- { + if !c.resolutions[i].memo { + continue + } + if outer == nil { + // Typically, the computation reads its own result, and no + // computation in between depends on it. + outer = c.memoEffectsAt(depth - 1) + } + f := c.memoEffectsAt(i) + if f == outer || outer.parent == f { + continue + } + if h := f.cycleHead(); h >= 0 && h <= head { + break + } + if i < c.effectsMarkDepth-1 { + // The resolution is in progress below the mark, so discarding to + // the mark must also discard this dependency. + c.logDepsDiscard(f) + } + rare := f.rareFields() + rare.cycles = append(rare.cycles, memoCycleDep{dep: int32(len(f.deps)), head: int32(head)}) + f.deps = append(f.deps, outer) + outer.parent = f + } +} + +// A memoCycleDep is a dependency memoCycle added on a cycle head. +type memoCycleDep struct { + dep int32 // Index in deps + head int32 // Index of the head's resolution +} + +// cycleHead returns the index of the resolution of the lowest cycle head that +// memoCycle made f depend on, or -1. +func (f *memoEffects) cycleHead() int { + if f.rare == nil || len(f.rare.cycles) == 0 { + return -1 + } + return int(f.rare.cycles[len(f.rare.cycles)-1].head) +} + +// logDepsDiscard logs the dependencies of a computation in progress below the +// innermost effects mark, which are about to grow, so discardSince can +// restore them. +// +//go:noinline +func (c *Checker) logDepsDiscard(f *memoEffects) { + n := len(f.deps) + c.discardLog = append(c.discardLog, discardEntry{effects: true, restore: func() { + if len(f.deps) > n { + f.truncateDeps(n) + } + }}) +} + +func (c *Checker) memoEffectsAt(i int) *memoEffects { + f := c.resolutions[i].effects + if f == nil { + f = &memoEffects{entry: c.resolutions[i].entry, entered: true} + c.resolutions[i].effects = f + c.attachDeferred(i, f) + } + return f +} + +// attachDeferred attaches the results the memo resolution at index i cached +// before it had effects f; see attachState. A result cached before an effects +// mark or a cycle is attached as if it had been attached then, so the discard +// log does not log it. +// +//go:noinline +func (c *Checker) attachDeferred(i int, f *memoEffects) { + start := int(c.resolutions[i].attachStart) + end := len(c.deferredAttach) + for j := i + 1; j < len(c.resolutions); j++ { + if c.resolutions[j].memo { + end = int(c.resolutions[j].attachStart) + break + } + } + if start == end { + return + } + for k := start; k < end; k++ { + d := c.deferredAttach[k] + c.deferredAttach[k] = deferredAttachment{} + switch { + case d.state != nil: + c.linkState(d.state, f, false) + case d.key != 0: + c.linkState(c.memoState(d.key), f, false) + } + } +} + +// Cycles +// +// A cached result must be the result of computing it from scratch, so that +// it does not depend on which computation happened to request it first. A +// computation that observes another computation still in progress (a +// circularity) sees a provisional state, so results cached from the moment +// the cycle is detected until the outermost computation of the cycle (its +// head) finishes may differ from computing them from scratch. Those results +// are discarded when the head finishes, and are recomputed when next +// requested. The computations that were in progress when the cycle was +// detected (its participants) are kept: each of them reaches the cycle when +// computed from scratch too, so each result is the fallback for a circularity +// whichever participant is computed first. +// +// Type comparisons are the exception: a comparison that reaches itself +// assumes it holds rather than being a circularity, since relations are +// coinductive. The Relater tracks those assumptions itself (see +// recursiveTypeRelatedTo), and caches only results that hold whatever they +// assumed. Each comparison is still a resolution, so circularities through +// other computations it needs are handled as above, discarding the relation +// results cached during them. A comparison that needs another relater, as in +// variance measurement, does not see the assumptions of the outer one, so it +// compares from scratch, as it would if run first. + +// Discard log +// +// Some computations run provisionally, and may have to be discarded as if +// they had never run: those in a cycle (see above), and those a computation +// that retries from a mark runs before retrying (see markDiscard). While any +// of them is in progress, what they cache is logged in the discard log, from +// which each discards to the position where it started. The discard log is +// cleared once nothing in progress can discard. + +type discardEntry struct { + memo *memoHeader // The memo restored, or nil + drop *memoEffects // Effects to drop when discarded; see logResultDiscard + restore func() + effects bool // Restores the effects of a result rather than the result +} + +// logResultDiscard appends a result restore entry to the discard log. If own, +// the innermost computation in progress computes the result, and if it read +// the incomplete result of a computation in progress (see markReadIncomplete) +// without reporting an error located at the current node, discarding the +// result also drops the computation's own effects: they may be derived from +// the incomplete result, so they would report errors that computing from +// complete results does not, while the results it reused are still replayed. +func (c *Checker) logResultDiscard(own bool, memo *memoHeader, restore func()) { + var drop *memoEffects + if own { + if f := &c.resolutions[c.memoTop-1]; f.readIncomplete && !f.reportsAtCurrentNode { + drop = f.effects + } + } + c.discardLog = append(c.discardLog, discardEntry{memo: memo, drop: drop, restore: restore}) +} + +func (c *Checker) logMemoDiscard(memo *memoHeader, key memoKey, restore func()) { + c.logResultDiscard(c.memoTop != 0 && c.resolutions[c.memoTop-1].source == nil && c.resolutions[c.memoTop-1].key == key, memo, restore) +} + +// logResults reports whether caching a result must be logged in the discard +// log. +func (c *Checker) logResults() bool { + return c.circularityStart != 0 || c.varianceCycleRoot != 0 +} + +// logEffects reports whether changing the effects of a result must be logged +// in the discard log. +func (c *Checker) logEffects() bool { + return c.circularityStart != 0 || c.effectsMarkDepth != 0 || c.varianceCycleRoot != 0 +} + +// discardLogSince restores everything logged in the discard log since +// position start. +func (c *Checker) discardLogSince(start int) { + start = min(start, len(c.discardLog)) + for i := range c.innerCycles { + c.innerCycles[i].mark = min(c.innerCycles[i].mark, start) + } + for i := range c.varianceStack { + c.varianceStack[i].discardLog = min(c.varianceStack[i].discardLog, start) + } + for i := len(c.discardLog) - 1; i >= start; i-- { + if d := c.discardLog[i].drop; d != nil { + d.dropped = true + } + c.discardLog[i].restore() + } + clear(c.discardLog[start:]) + c.discardLog = c.discardLog[:start] +} + +// trimDiscardLog clears the discard log if nothing in progress can discard. +func (c *Checker) trimDiscardLog() { + if !c.logEffects() && len(c.discardLog) != 0 { + for i := range c.varianceStack { + c.varianceStack[i].discardLog = 0 + } + clear(c.discardLog) + c.discardLog = c.discardLog[:0] + } +} + +// A flag memo is a cached result held outside a memo, so that it costs no +// space of its own: bits of a flags word shared with other results, or a +// field of storage allocated only when needed. It is accounted for like a +// memo under a key naming its entity and field. Readers finding it cached +// call observeFlagMemo; it is computed between beginMemo and endMemo for the +// key, and cached with setFlagMemoBits or setFlagMemoValue, or with +// setFlagMemo after logging how to discard it with logMemoDiscard. + +// observeFlagMemo accounts for reading the flag memo identified by key. +func (c *Checker) observeFlagMemo(key memoKey) { + if c.observing() { + c.observeMemo(key) + } +} + +// setFlagMemo accounts for caching the result of the flag memo identified by +// key, as memo.set does. +func (c *Checker) setFlagMemo(key memoKey) { + if !c.checking { + c.memoAttach(key) + } +} + +// logRestore logs how to discard part of a result, or state derived from +// results, in the discard log; the caller checks logResults so that the +// closure is only allocated when needed. +// +//go:noinline +func (c *Checker) logRestore(restore func()) { + c.discardLog = append(c.discardLog, discardEntry{restore: restore}) +} + +// objectFlagsKnown reports whether the flag memo of t whose result is cached +// once computed is set can be read without objectFlagsCell, which callers +// check first since objectFlagsCell is too large to inline. +func (c *Checker) objectFlagsKnown(t *Type, computed ObjectFlags) bool { + return t.objectFlags&computed != 0 && !c.observingMemos +} + +// objectFlagsCell returns the flags under value of t, a flag memo whose +// result is cached once the computed flag is set, computing them in a +// computation of their own if needed. With inProgress, the computed flag is +// set while computing, so that a computation re-entering it finds no flags. +func (c *Checker) objectFlagsCell(t *Type, field memoField, computed ObjectFlags, value ObjectFlags, inProgress bool, compute func() ObjectFlags) ObjectFlags { + key := memoKeyOf(t, field) + if t.objectFlags&computed != 0 { + c.observeMemo(key) + return t.objectFlags & value + } + c.beginMemo(key) + if inProgress { + setFlagMemoBits(c, key, &t.objectFlags, computed|value, computed) + } + result := compute() & value + if inProgress || t.objectFlags&computed == 0 { + setFlagMemoBits(c, key, &t.objectFlags, computed|value, computed|result) + } else { + result = t.objectFlags & value + } + c.endMemo() + return result +} + +// setFlagMemoBits caches the bits of *p under mask as the result of the flag +// memo identified by key. +func setFlagMemoBits[T ~uint32](c *Checker, key memoKey, p *T, mask T, bits T) { + if c.logResults() { + old := *p & mask + c.logMemoDiscard(nil, key, func() { *p = *p&^mask | old }) + } + *p = *p&^mask | bits&mask + c.setFlagMemo(key) +} + +// setFlagMemoValue caches v in *p as the result of the flag memo identified +// by key. +func (c *Checker) setFlagMemoValue[T any](key memoKey, p *T, v T) { + if c.logResults() { + old := *p + c.logMemoDiscard(nil, key, func() { *p = old }) + } + *p = v + c.setFlagMemo(key) +} + +// endCircularity discards the results cached since the outermost cycle in +// progress was detected, except those of its participants. +func (c *Checker) endCircularity() { + mark := min(c.circularityMark, len(c.discardLog)) + c.circularityStart = 0 + c.innerCycles = c.innerCycles[:0] + c.discardCycle(mark, false) + c.cycleParticipants = nil + c.trimDiscardLog() + c.resumeChecking() +} + +// discardCycle discards the results logged since mark, except those of the +// participants of the cycle in progress. If retain, the entries not undone +// stay in the log, as the outer cycle may still discard them. +func (c *Checker) discardCycle(mark int, retain bool) { + undo := c.discardLog[mark:] + participants := c.cycleParticipants + keep := make(map[*memoHeader]struct{}, len(participants)) + for _, p := range participants { + if p != nil { + keep[p] = struct{}{} + } + } + n := mark + for i := len(undo) - 1; i >= 0; i-- { + u := undo[i] + if u.effects { + // A result recomputed after the cycle also replays the effects + // of the computation discarded here. + continue + } + if _, ok := keep[u.memo]; ok && u.memo != nil { + continue + } + if u.drop != nil { + u.drop.dropped = true + } + u.restore() + undo[i] = discardEntry{} + } + if retain { + for _, u := range undo { + if u.restore != nil { + c.discardLog[n] = u + n++ + } + } + } + clear(c.discardLog[n:]) + c.discardLog = c.discardLog[:n] +} + +// extendCircularity makes the resolution at index head-1 part of the cycle in +// progress, starting one if there is none. A cycle whose head is inside the +// one in progress is an inner cycle: what is computed from its head while it +// is incomplete is discarded when it finishes, as for the outermost, rather +// than read by the rest of the outer cycle. +func (c *Checker) extendCircularity(head int) { + c.observedHead = minObservedHead(c.observedHead, int32(head)) + c.recordFromCheck() + switch { + case c.circularityStart == 0: + c.circularityMark = len(c.discardLog) + c.circularityStart = head + case head < c.circularityStart: + c.addInnerCycle(c.circularityStart, c.circularityMark) + c.circularityStart = head + case head > c.circularityStart: + c.addInnerCycle(head, len(c.discardLog)) + } +} + +type innerCycle struct { + head int // As Checker.circularityStart + mark int // Length of the discard log when detected +} + +func (c *Checker) addInnerCycle(head int, mark int) { + for _, ic := range c.innerCycles { + if ic.head == head { + return + } + } + c.innerCycles = append(c.innerCycles, innerCycle{head, mark}) +} + +// dropResolution removes the resolution at index n, the top of the stack, and +// ends the cycles it was the head of. +func (c *Checker) dropResolution(n int) { + c.resolutions[n] = resolution{} + c.resolutions = c.resolutions[:n] + c.endCyclesAt(n) +} + +// endCyclesAt ends the cycles whose head is the resolution at index n, which +// just finished. +func (c *Checker) endCyclesAt(n int) { + if c.circularityStart > n { + c.endCircularity() + } else if len(c.innerCycles) != 0 { + c.endInnerCycles(n) + } +} + +// endInnerCycles ends the inner cycles whose head is the resolution at index +// n, which just finished. +func (c *Checker) endInnerCycles(n int) { + for i, ic := range slices.Backward(c.innerCycles) { + if ic.head > n { + c.innerCycles = slices.Delete(c.innerCycles, i, i+1) + c.discardCycle(min(ic.mark, len(c.discardLog)), true) + } + } +} + +// A discardMark is a position in the computation in progress to which it can +// discard, so that it can run again from the same state, as if what it +// computed since had never run. From markDiscard to releaseDiscardMark, the +// discard log logs all that may have to be discarded. +type discardMark struct { + discardPoint + effectsMarkDepth int // Checker.effectsMarkDepth to restore on release + + // The effects recorded so far by the innermost computation in progress, + // which must have a memo resolution. + recording *memoEffects + effects int + deps int + resolutionIndex int +} + +func (c *Checker) markDiscard() discardMark { + c.recordFromCheck() + n := c.memoTop - 1 + f := c.memoEffectsAt(n) + m := discardMark{ + discardPoint: c.discardPoint(), + effectsMarkDepth: c.effectsMarkDepth, + recording: f, + effects: len(f.effects), + deps: len(f.deps), + resolutionIndex: n, + } + c.effectsMarkDepth = n + 1 + return m +} + +// discardSince discards the results cached since m, with the effects of +// computing them, restoring the cycle in progress then, and discards the +// effects recorded by the computation in progress. +func (c *Checker) discardSince(m discardMark) { + c.discardTo(m.discardPoint) + debug.Assert(c.memoTop-1 == m.resolutionIndex && c.resolutions[m.resolutionIndex].effects == m.recording, "effects discarded outside their computation") + m.recording.truncate(m.effects, m.deps) +} + +// A discardPoint is a position in the computation in progress to which what +// it caches since can be discarded. +type discardPoint struct { + discardLog int + participants int + circularityStart int +} + +func (c *Checker) discardPoint() discardPoint { + return discardPoint{discardLog: len(c.discardLog), participants: len(c.cycleParticipants), circularityStart: c.circularityStart} +} + +// discardTo discards the results cached since p, with the effects of +// computing them, restoring the cycle in progress then. +func (c *Checker) discardTo(p discardPoint) { + c.discardLogSince(p.discardLog) + c.cycleParticipants = c.cycleParticipants[:min(p.participants, len(c.cycleParticipants))] + c.circularityStart = p.circularityStart +} + +// truncate discards the effects recorded in f since it had the given numbers +// of effects and dependencies. +func (f *memoEffects) truncate(effects int, deps int) { + clear(f.effects[effects:]) + f.effects = f.effects[:effects] + f.truncateDeps(deps) +} + +// releaseDiscardMark ends the period during which the computation in progress +// can discard to m. +func (c *Checker) releaseDiscardMark(m discardMark) { + c.effectsMarkDepth = m.effectsMarkDepth + c.trimDiscardLog() + c.resumeChecking() +} + +func (f *memoEffects) truncateDeps(n int) { + for _, d := range f.deps[n:] { + if d.parent == f { + d.parent = nil + } + } + clear(f.deps[n:]) + f.deps = f.deps[:n] + if rare := f.rare; rare != nil { + for len(rare.cycles) != 0 && int(rare.cycles[len(rare.cycles)-1].dep) >= n { + rare.cycles = rare.cycles[:len(rare.cycles)-1] + } + } +} + +// logPendingDiscard logs the effects of the result in the memo state, which +// are about to change, so discardSince can restore them. +// +//go:noinline +func (c *Checker) logPendingDiscard(state *memoState) { + old := state.pending + n := 0 + if old != nil { + n = len(old.deps) + } + c.discardLog = append(c.discardLog, discardEntry{effects: true, restore: func() { + switch { + case old == nil && state.pending != nil: + c.memoPendingCount-- + c.syncObserving() + case old != nil && state.pending == nil: + c.memoPendingCount++ + c.syncObserving() + } + state.pending = old + // Only a set of deps (see linkState) gains deps through its state. + if old != nil && !old.entered && len(old.deps) > n { + old.truncateDeps(n) + } + }}) +} + +// Instantiation limits +// +// Instantiation stops with an error when nested too deeply or after too +// many instantiations, as the instantiation is then very likely infinite. +// For the result not to depend on what was cached before, each memoized +// computation (including each cached comparison of two types) measures +// depth from where it started, whether or not it reports errors, and a +// reused result adds no depth. Chains that start a new memoized computation +// at every level are only stopped by a much larger limit on the whole +// outermost instantiation, which is only reached by runaway instantiations, +// and which does depend on what was cached before. +// +// The limit on the number of instantiations is likewise only reached by +// runaway instantiations, and depends on what was cached before: it counts +// the instantiations performed since the start of the current statement or +// expression, or of the outermost computation in progress, and reused +// results add none. Making it independent would mean +// keeping, for every cached result, the work needed to compute it from +// scratch, which costs far more than the limit is worth. + +const ( + instantiationDepthLimit = 100 + instantiationTotalDepthLimit = 200 + instantiationCountLimit = 5_000_000 +) + +func (c *Checker) instantiationDepthLimitReached() bool { + depth := len(c.instantiationStack) + return depth-int(c.currentResolution().depthStart) == instantiationDepthLimit || depth == instantiationTotalDepthLimit +} diff --git a/tsc/internal/checker/nodebuilder_hover.go b/tsc/internal/checker/nodebuilder_hover.go index b49ac981fbd41..9abe67c6b1ed4 100644 --- a/tsc/internal/checker/nodebuilder_hover.go +++ b/tsc/internal/checker/nodebuilder_hover.go @@ -263,7 +263,7 @@ func (b *NodeBuilderImpl) expandInterfaceDecl(symbol *ast.Symbol) *ast.Node { members = append(members, b.signatureToSignatureDeclarationHelper(sig, ast.KindCallSignature, nil)) } // Properties, filtering inherited - filteredProps := b.filterInheritedProperties(interfaceType, baseTypes, resolved.getProperties()) + filteredProps := b.filterInheritedProperties(interfaceType, baseTypes, resolved.getProperties(b.ch)) members = b.serializePropertiesWithTruncation(filteredProps, members) // Heritage clauses diff --git a/tsc/internal/checker/nodebuilderimpl.go b/tsc/internal/checker/nodebuilderimpl.go index 7c90f3f320fbc..595d2aecbf887 100644 --- a/tsc/internal/checker/nodebuilderimpl.go +++ b/tsc/internal/checker/nodebuilderimpl.go @@ -519,7 +519,7 @@ func (b *NodeBuilderImpl) tryGetResolvedSymbolFromTypeNode(node *ast.Node) *ast. if links == nil { return nil } - return links.resolvedSymbol + return links.resolvedSymbol.get(b.ch) } func (b *NodeBuilderImpl) existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing *ast.TypeNode, t *Type) bool { @@ -576,7 +576,7 @@ func (b *NodeBuilderImpl) getResolvedTypeWithoutAbstractConstructSignatures(t *S t.extra.objectTypeWithoutAbstractConstructSignatures = t.AsType() return t.AsType() } - typeCopy := b.ch.newAnonymousType(t.symbol, t.getMembers(), t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.getIndexInfos()) + typeCopy := b.ch.newAnonymousType(t.symbol, t.getMembers(b.ch), t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.getIndexInfos()) t.extra.objectTypeWithoutAbstractConstructSignatures = typeCopy typeCopy.AsStructuredType().ensureExtra().objectTypeWithoutAbstractConstructSignatures = typeCopy return typeCopy @@ -2063,9 +2063,9 @@ func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, name, checkFlags) links := c.valueSymbolLinks.Get(symbol) if flags&ElementFlagsRest != 0 { - links.resolvedType = c.createArrayType(t) + links.resolvedType.set(c, c.createArrayType(t)) } else { - links.resolvedType = t + links.resolvedType.set(c, t) } return symbol }) @@ -2591,6 +2591,16 @@ func (b *NodeBuilderImpl) getPropertyNameNodeForSymbolFromNameType(symbol *ast.S return nil } +// approximateNameLength returns the length of a property name, without the +// symbol ID a late-bound name ends with, which depends on when the symbol was +// created and so would make truncation depend on the order of checking. +func approximateNameLength(name string) int { + if isLateBoundName(name) { + return strings.LastIndexByte(name, '@') + } + return len(name) +} + func (b *NodeBuilderImpl) addPropertyToElementList(propertySymbol *ast.Symbol, typeElements []*ast.TypeElement) []*ast.TypeElement { propertyIsReverseMapped := propertySymbol.CheckFlags()&ast.CheckFlagsReverseMapped != 0 var propertyType *Type @@ -2627,7 +2637,7 @@ func (b *NodeBuilderImpl) addPropertyToElementList(propertySymbol *ast.Symbol, t } propertyName := b.getPropertyNameNodeForSymbol(propertySymbol, saveEnclosingDeclaration) b.ctx.enclosingDeclaration = saveEnclosingDeclaration - b.ctx.approximateLength += len(ast.SymbolName(propertySymbol)) + 1 + b.ctx.approximateLength += approximateNameLength(ast.SymbolName(propertySymbol)) + 1 if propertySymbol.Flags()&ast.SymbolFlagsAccessor != 0 { writeType := b.ch.getWriteTypeOfSymbol(propertySymbol) @@ -2669,7 +2679,7 @@ func (b *NodeBuilderImpl) addPropertyToElementList(propertySymbol *ast.Symbol, t typeElements = append(typeElements, fakeGetterDeclaration) setterParam := b.ch.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "arg") - b.ch.valueSymbolLinks.Get(setterParam).resolvedType = writeType + b.ch.valueSymbolLinks.Get(setterParam).resolvedType.set(b.ch, writeType) fakeSetterSignature := b.ch.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{setterParam}, b.ch.voidType, nil, 0) fakeSetterDeclaration := b.signatureToSignatureDeclarationHelper(fakeSetterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{ name: propertyName, @@ -2758,7 +2768,7 @@ func (b *NodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *Structur typeElements = slices.Concat(typeElements, b.indexInfoToObjectComputedNamesOrSignatureDeclaration(info, typeNode)) } - properties := resolvedType.getProperties() + properties := resolvedType.getProperties(b.ch) if len(properties) == 0 { return b.f.NewNodeList(typeElements) } @@ -2800,14 +2810,14 @@ func (b *NodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *Structur } func (b *NodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { - if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError) { + if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError.get(b.ch)) { return b.createMappedTypeNodeFromType(t) } resolved := b.ch.resolveStructuredTypeMembers(t) callSigs := resolved.CallSignatures() ctorSigs := resolved.ConstructSignatures() - if len(resolved.getProperties()) == 0 && len(resolved.getIndexInfos()) == 0 { + if len(resolved.getProperties(b.ch)) == 0 && len(resolved.getIndexInfos()) == 0 { if len(callSigs) == 0 && len(ctorSigs) == 0 { b.ctx.approximateLength += 2 result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{})) @@ -2836,9 +2846,9 @@ func (b *NodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { return b.ch.getOrCreateTypeFromSignature(s) }) // count the number of type elements excluding abstract constructors - typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.getIndexInfos()) + core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.getProperties(), func(p *ast.Symbol) bool { + typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.getIndexInfos()) + core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.getProperties(b.ch), func(p *ast.Symbol) bool { return p.Flags()&ast.SymbolFlagsPrototype == 0 - }), len(resolved.getProperties())) + }), len(resolved.getProperties(b.ch))) // don't include an empty object literal if there were no other static-side // properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}` // and not `(abstract new () => {}) & {}` diff --git a/tsc/internal/checker/printer.go b/tsc/internal/checker/printer.go index b84c967883d88..7132ae615e70e 100644 --- a/tsc/internal/checker/printer.go +++ b/tsc/internal/checker/printer.go @@ -60,7 +60,7 @@ func (c *Checker) typeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags // Serialization of types can lead to (lazy) resolution of members, which can cause diagnostics that again require // serialization of types. This can potentially result in infinite recursion and stack overflows. To prevent that, // after a certain number of recursive invocations the function simply returns "?". - if c.serializationLevel >= maxSerializationLevel { + if c.depthLimitReached(&c.serializationLevel, maxSerializationLevel) { return "?" } newLine := "" @@ -80,9 +80,9 @@ func (c *Checker) typeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags defer func() { nodeBuilder.verbosity = oldVerbosity }() - c.serializationLevel++ + c.enterDepthLimit(&c.serializationLevel) typeNode := nodeBuilder.TypeToTypeNode(t, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) - c.serializationLevel-- + c.serializationLevel.exit() if typeNode == nil { panic("should always get typenode") } diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index e5b173a299b10..b6a159dadba5e 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -72,8 +72,11 @@ const ( RelationComparisonResultReportsUnmeasurable RelationComparisonResult = 1 << 3 RelationComparisonResultReportsUnreliable RelationComparisonResult = 1 << 4 RelationComparisonResultComplexityOverflow RelationComparisonResult = 1 << 5 - RelationComparisonResultReportsMask = RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable - RelationComparisonResultOverflow = RelationComparisonResultComplexityOverflow + // The comparison observed incomplete variances; see getRecursiveVariances. + // Only reported, never cached. + RelationComparisonResultReportsIncomplete RelationComparisonResult = 1 << 6 + RelationComparisonResultReportsMask = RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable + RelationComparisonResultOverflow = RelationComparisonResultComplexityOverflow ) type DiagnosticAndArguments struct { @@ -97,23 +100,26 @@ func asRecursionId[T *ast.Node | *ast.Symbol | *Type](value T) RecursionId { return RecursionId{value: value} } -type Relation struct { - results map[CacheHashKey]RelationComparisonResult +// relationCountLimit is the number of results a single comparison may add to +// a relation before it is considered too complex. +const relationCountLimit = 16_000_000 / 8 + +// relationKeyBits returns well-mixed bits of a relation key, which is a hash. +func relationKeyBits(k CacheHashKey) uint64 { + return k.Lo } -func (r *Relation) get(key CacheHashKey) RelationComparisonResult { - return r.results[key] +type Relation struct { + results memoMap[CacheHashKey, RelationComparisonResult] } -func (r *Relation) set(key CacheHashKey, result RelationComparisonResult) { - if r.results == nil { - r.results = make(map[CacheHashKey]RelationComparisonResult) - } - r.results[key] = result +func (r *Relation) get(c *Checker, key CacheHashKey) RelationComparisonResult { + result, _ := r.results.get(c, key) + return result } -func (r *Relation) size() int { - return len(r.results) +func (r *Relation) set(c *Checker, key CacheHashKey, result RelationComparisonResult) { + r.results.set(c, key, result) } func (c *Checker) isTypeIdenticalTo(source *Type, target *Type) bool { @@ -192,11 +198,14 @@ func (c *Checker) isTypeRelatedTo(source *Type, target *Type, relation *Relation } } if source.flags&TypeFlagsObject != 0 && target.flags&TypeFlagsObject != 0 { - id, _ := getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false) - related := relation.get(id) - if related != RelationComparisonResultNone { + id, constrained := c.getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false) + // An overflow is reported by each comparison that reaches it, so it + // is left to checkTypeRelatedTo. + related := relation.get(c, id) + if related != RelationComparisonResultNone && related&RelationComparisonResultOverflow == 0 { return related&RelationComparisonResultSucceeded != 0 } + return c.checkTypeRelatedToWithKey(source, target, relation, nil /*errorNode*/, nil, nil, true, id, constrained, related) } if source.flags&TypeFlagsStructuredOrInstantiable != 0 || target.flags&TypeFlagsStructuredOrInstantiable != 0 { return c.checkTypeRelatedTo(source, target, relation, nil /*errorNode*/) @@ -290,9 +299,22 @@ func (c *Checker) isEnumTypeRelatedTo(source *ast.Symbol, target *ast.Symbol, er return false } key := EnumRelationKey{sourceId: ast.GetSymbolId(sourceSymbol), targetId: ast.GetSymbolId(targetSymbol)} - if entry := c.enumRelation[key]; entry != RelationComparisonResultNone && !(entry&RelationComparisonResultFailed != 0 && errorReporter != nil) { - return entry&RelationComparisonResultSucceeded != 0 - } + entry, _ := c.enumRelation.get(c, key) + if entry != RelationComparisonResultNone { + if entry&RelationComparisonResultFailed == 0 || errorReporter == nil { + return entry&RelationComparisonResultSucceeded != 0 + } + // Recompute a cached failure only to elaborate it. + return c.isEnumTypeRelatedToUncached(sourceSymbol, targetSymbol, errorReporter) + } + c.enumRelation.begin(c, key) + related := c.isEnumTypeRelatedToUncached(sourceSymbol, targetSymbol, errorReporter) + c.enumRelation.set(c, key, core.IfElse(related, RelationComparisonResultSucceeded, RelationComparisonResultFailed)) + c.endMemo() + return related +} + +func (c *Checker) isEnumTypeRelatedToUncached(sourceSymbol *ast.Symbol, targetSymbol *ast.Symbol, errorReporter ErrorReporter) bool { targetEnumType := c.getTypeOfSymbol(targetSymbol) for _, sourceProperty := range c.getPropertiesOfType(c.getTypeOfSymbol(sourceSymbol)) { if sourceProperty.Flags()&ast.SymbolFlagsEnumMember != 0 { @@ -301,7 +323,6 @@ func (c *Checker) isEnumTypeRelatedTo(source *ast.Symbol, target *ast.Symbol, er if errorReporter != nil { errorReporter(diagnostics.Property_0_is_missing_in_type_1, c.symbolToString(sourceProperty), c.TypeToStringEx(c.getDeclaredTypeOfSymbol(targetSymbol), nil /*enclosingDeclaration*/, TypeFormatFlagsUseFullyQualifiedType, nil)) } - c.enumRelation[key] = RelationComparisonResultFailed return false } sourceValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(sourceProperty, ast.KindEnumMember)).Value @@ -312,7 +333,6 @@ func (c *Checker) isEnumTypeRelatedTo(source *ast.Symbol, target *ast.Symbol, er if errorReporter != nil { errorReporter(diagnostics.Each_declaration_of_0_1_differs_in_its_value_where_2_was_expected_but_3_was_given, c.symbolToString(targetSymbol), c.symbolToString(targetProperty), c.valueToString(targetValue), c.valueToString(sourceValue)) } - c.enumRelation[key] = RelationComparisonResultFailed return false } // At this point we know that at least one of the values is 'undefined'. @@ -328,13 +348,11 @@ func (c *Checker) isEnumTypeRelatedTo(source *ast.Symbol, target *ast.Symbol, er knownStringValue := core.OrElse(sourceValue, targetValue) errorReporter(diagnostics.One_value_of_0_1_is_the_string_2_and_the_other_is_assumed_to_be_an_unknown_numeric_value, c.symbolToString(targetSymbol), c.symbolToString(targetProperty), c.valueToString(knownStringValue)) } - c.enumRelation[key] = RelationComparisonResultFailed return false } } } } - c.enumRelation[key] = RelationComparisonResultSucceeded return true } @@ -364,23 +382,66 @@ func (c *Checker) checkTypeRelatedToEx( errorNode *ast.Node, headMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic, +) bool { + return c.checkTypeRelatedToWithKey(source, target, relation, errorNode, headMessage, diagnosticOutput, false, CacheHashKey{}, false, RelationComparisonResultNone) +} + +// checkTypeRelatedToWithKey is checkTypeRelatedToEx given, if hasKey, the +// relation key of source and target, as returned by getRelationKey, and the +// result cached under it. +func (c *Checker) checkTypeRelatedToWithKey( + source *Type, + target *Type, + relation *Relation, + errorNode *ast.Node, + headMessage *diagnostics.Message, + diagnosticOutput *[]*ast.Diagnostic, + hasKey bool, + id CacheHashKey, + constrained bool, + entry RelationComparisonResult, ) bool { r := c.getRelater() r.relation = relation r.errorNode = errorNode - r.relationCount = (16_000_000 - relation.size()) / 8 - result := r.isRelatedToEx(source, target, RecursionFlagsBoth, errorNode != nil /*reportErrors*/, headMessage, IntersectionStateNone) + // The budget does not depend on how many results are cached, so that whether + // a comparison overflows does not depend on what was compared before it. + r.relationCount = relationCountLimit + r.topSource, r.topTarget = source, target + if hasKey { + r.topKey, r.topConstrained, r.topHasKey = id, constrained, true + r.topEntry, r.topEntryWrites, r.topEntryRead = entry, relation.results.writes, true + } + var result Ternary + if entry&RelationComparisonResultOverflow != 0 { + // The comparison overflowed before, and would again. + r.overflow = true + } else { + saveIncomplete := c.reliabilityFlags & RelationComparisonResultReportsIncomplete + c.reliabilityFlags &^= RelationComparisonResultReportsIncomplete + result = r.isRelatedToEx(source, target, RecursionFlagsBoth, errorNode != nil /*reportErrors*/, headMessage, IntersectionStateNone) + c.reliabilityFlags = c.reliabilityFlags&^RelationComparisonResultReportsIncomplete | saveIncomplete + if r.topBegun { + if r.overflow { + // Record this relation as having failed such that we don't attempt the overflowing operation again. + relation.set(c, r.topKey, RelationComparisonResultFailed|RelationComparisonResultComplexityOverflow) + } + c.endMemo() + } + } 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*/) - relation.set(id, RelationComparisonResultFailed|RelationComparisonResultComplexityOverflow) if tr := c.tracer; tr != nil { tr.Instant(tracing.PhaseCheckTypes, "checkTypeRelatedTo_DepthLimit", map[string]any{"sourceId": source.id, "targetId": target.id, "depth": len(r.sourceStack), "targetDepth": len(r.targetStack)}) } - if errorNode == nil { - errorNode = c.currentNode + message, sourceString, targetString := diagnostics.Excessive_complexity_comparing_types_0_and_1, c.TypeToString(source), c.TypeToString(target) + switch { + case errorNode != nil: + c.reportDiagnostic(NewDiagnosticForNode(errorNode, message, sourceString, targetString), diagnosticOutput) + case diagnosticOutput != nil: + c.reportDiagnostic(NewDiagnosticForNode(c.currentNode, message, sourceString, targetString), diagnosticOutput) + default: + c.errorAtCurrentNode(message, sourceString, targetString) } - c.reportDiagnostic(NewDiagnosticForNode(errorNode, diagnostics.Excessive_complexity_comparing_types_0_and_1, c.TypeToString(source), c.TypeToString(target)), diagnosticOutput) } else if r.errorChain != nil { // Check if we should issue an extra diagnostic to produce a quickfix for a slightly incorrect import statement if headMessage != nil && errorNode != nil && result == TernaryFalse && source.symbol != nil && c.exportTypeLinks.Has(source.symbol) { @@ -767,7 +828,7 @@ func isExcessPropertyCheckTarget(t *Type) bool { // structurally equal to at least maxDepth levels, but unequal at some level beyond that. func (c *Checker) isDeeplyNestedType(t *Type, stack []*Type, maxDepth int) bool { if len(stack) >= maxDepth { - target := getRecursionIdentityTarget(t) + target := c.getRecursionIdentityTarget(t) if target.flags&TypeFlagsIntersection != 0 { for _, t := range target.Types() { if c.isDeeplyNestedType(t, stack, maxDepth) { @@ -779,7 +840,7 @@ func (c *Checker) isDeeplyNestedType(t *Type, stack []*Type, maxDepth int) bool count := 0 lastGeneration := 0 for _, t := range stack { - if hasMatchingRecursionIdentity(t, identity) { + if c.hasMatchingRecursionIdentity(t, identity) { // We only count occurrences of a generation at least that of the previous occurrence, since // newer instantiations caused by recursion are built from the previous ones, whereas nested // type arguments are older than the types they are arguments of. @@ -818,7 +879,7 @@ func (c *Checker) typeGeneration(t *Type) int { switch { case t.flags&TypeFlagsObject != 0: if t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil { - generation = c.typesGeneration(t.AsTypeReference().resolvedTypeArguments) + generation = c.typesGeneration(t.AsTypeReference().resolvedTypeArguments.peek()) } else if m := t.AsObjectType().mapper; m != nil { generation = c.mapperGeneration(m) } else if t.symbol != nil && t.symbol.Flags()&ast.SymbolFlagsObjectLiteral != 0 { @@ -828,7 +889,7 @@ func (c *Checker) typeGeneration(t *Type) int { if prop.Flags()&ast.SymbolFlagsTransient == 0 || prop.CheckFlags()&(ast.CheckFlagsInstantiated|ast.CheckFlagsMapped|ast.CheckFlagsReverseMapped|ast.CheckFlagsDeferredType) != 0 { continue } - if propType := c.valueSymbolLinks.Get(prop).resolvedType; propType != nil { + if propType := c.peekTypeOfObjectLiteralMember(prop); propType != nil { generation = max(generation, c.typeGeneration(propType)+1) } } @@ -896,11 +957,11 @@ func (c *Checker) deeplyNestedStart(t *Type, stack []*Type, maxDepth int) int { return 0 } -func hasMatchingRecursionIdentity(t *Type, identity RecursionId) bool { - target := getRecursionIdentityTarget(t) +func (c *Checker) hasMatchingRecursionIdentity(t *Type, identity RecursionId) bool { + target := c.getRecursionIdentityTarget(t) if target.flags&TypeFlagsIntersection != 0 { for _, t := range target.Types() { - if hasMatchingRecursionIdentity(t, identity) { + if c.hasMatchingRecursionIdentity(t, identity) { return true } } @@ -909,8 +970,8 @@ func hasMatchingRecursionIdentity(t *Type, identity RecursionId) bool { return getRecursionIdentityFromTarget(target) == identity } -func getRecursionIdentity(t *Type) RecursionId { - return getRecursionIdentityFromTarget(getRecursionIdentityTarget(t)) +func (c *Checker) getRecursionIdentity(t *Type) RecursionId { + return getRecursionIdentityFromTarget(c.getRecursionIdentityTarget(t)) } // Get the recursion identity target type from a type. Recursively (a) obtain the target object type of an @@ -919,15 +980,15 @@ func getRecursionIdentity(t *Type) RecursionId { // to explicitly written object literals. For example in `Mapped<{ x: Mapped<{ x: Mapped<{ x: string }>}>}>`, // each of the mapped type applications will have a unique recursion identity (that of their target object type // literal) and thus avoid appearing deeply nested. -func getRecursionIdentityTarget(t *Type) *Type { +func (c *Checker) getRecursionIdentityTarget(t *Type) *Type { if t.flags&TypeFlagsIndexedAccess != 0 { - return getRecursionIdentityTarget(t.AsIndexedAccessType().objectType) + return c.getRecursionIdentityTarget(t.AsIndexedAccessType().objectType) } if t.objectFlags&ObjectFlagsInstantiatedMapped == ObjectFlagsInstantiatedMapped { - target := t.checker.getModifiersTypeFromMappedType(t) + target := c.getModifiersTypeFromMappedType(t) if target != nil && (target.symbol != nil || target.flags&TypeFlagsIntersection != 0 && core.Some(target.Types(), func(t *Type) bool { return t.symbol != nil })) { - return getRecursionIdentityTarget(target) + return c.getRecursionIdentityTarget(target) } } return t @@ -1185,18 +1246,35 @@ func (c *Checker) isDiscriminantProperty(t *Type, name string) bool { if t != nil && t.flags&TypeFlagsUnion != 0 { prop := c.getUnionOrIntersectionProperty(t, name, false /*skipObjectFunctionPropertyAugment*/) if prop != nil && prop.CheckFlags()&ast.CheckFlagsSyntheticProperty != 0 { - if prop.CheckFlags()&ast.CheckFlagsIsDiscriminantComputed == 0 { - prop.SetCheckFlags(prop.CheckFlags() | ast.CheckFlagsIsDiscriminantComputed) - if prop.CheckFlags()&ast.CheckFlagsNonUniformAndLiteral == ast.CheckFlagsNonUniformAndLiteral && !c.isGenericType(c.getTypeOfSymbol(prop)) { - prop.SetCheckFlags(prop.CheckFlags() | ast.CheckFlagsIsDiscriminant) - } + key := memoKeyOf(prop, memoDiscriminant) + if prop.CheckFlags()&ast.CheckFlagsIsDiscriminantComputed != 0 { + c.observeFlagMemo(key) + return prop.CheckFlags()&ast.CheckFlagsIsDiscriminant != 0 } + c.beginMemo(key) + c.setDiscriminantFlags(prop, ast.CheckFlagsIsDiscriminantComputed) + if prop.CheckFlags()&ast.CheckFlagsNonUniformAndLiteral == ast.CheckFlagsNonUniformAndLiteral && !c.isGenericType(c.getTypeOfSymbol(prop)) { + c.setDiscriminantFlags(prop, ast.CheckFlagsIsDiscriminantComputed|ast.CheckFlagsIsDiscriminant) + } + c.endMemo() return prop.CheckFlags()&ast.CheckFlagsIsDiscriminant != 0 } } return false } +// setDiscriminantFlags caches whether the synthetic property prop is a +// discriminant, a flag memo. +func (c *Checker) setDiscriminantFlags(prop *ast.Symbol, flags ast.CheckFlags) { + const mask = ast.CheckFlagsIsDiscriminantComputed | ast.CheckFlagsIsDiscriminant + if c.logResults() { + old := prop.CheckFlags() & mask + c.logMemoDiscard(nil, memoKeyOf(prop, memoDiscriminant), func() { prop.SetCheckFlags(prop.CheckFlags()&^mask | old) }) + } + prop.SetCheckFlags(prop.CheckFlags()&^mask | flags) + c.setFlagMemo(memoKeyOf(prop, memoDiscriminant)) +} + func (c *Checker) getMatchingUnionConstituentForType(unionType *Type, t *Type) *Type { keyPropertyName := c.getKeyPropertyName(unionType) if keyPropertyName == "" { @@ -1214,13 +1292,15 @@ func (c *Checker) getMatchingUnionConstituentForType(unionType *Type, t *Type) * // an empty string if no such discriminant property exists. func (c *Checker) getKeyPropertyName(t *Type) string { u := t.AsUnionType() - if u.keyPropertyName == "" { - u.keyPropertyName, u.constituentMap = c.computeKeyPropertyNameAndMap(t) - } - if u.keyPropertyName == ast.InternalSymbolNameMissing { + name := c.memoCompute(&u.keyPropertyName, func() string { + var name string + name, u.constituentMap = c.computeKeyPropertyNameAndMap(t) + return name + }) + if name == ast.InternalSymbolNameMissing { return "" } - return u.keyPropertyName + return name } // Given a union type for which getKeyPropertyName returned a non-empty string, return the constituent @@ -1427,7 +1507,7 @@ func (c *Checker) getVariances(t *Type) []VarianceFlags { } func (c *Checker) getAliasVariances(symbol *ast.Symbol) []VarianceFlags { - return c.getVariancesWorker(symbol, c.typeAliasLinks.Get(symbol).typeParameters) + return c.getVariancesWorker(symbol, c.getTypeParametersOfTypeAlias(symbol)) } // Return an array containing the variance of each type parameter. The variance is effectively @@ -1437,114 +1517,220 @@ func (c *Checker) getAliasVariances(symbol *ast.Symbol) []VarianceFlags { // returns an empty slice when invoked recursively for the given generic type. func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type) []VarianceFlags { links := c.varianceLinks.Get(symbol) - if links.variances == nil { - var traceArgs map[string]any - if tr := c.tracer; tr != nil { - traceArgs = map[string]any{"arity": len(typeParameters), "id": c.getDeclaredTypeOfSymbol(symbol).id} - popFn := tr.Push(tracing.PhaseCheckTypes, "getVariancesWorker", traceArgs, true) - defer func() { - formatted := make([]string, len(links.variances)) - for i, v := range links.variances { - formatted[i] = v.String() - } - traceArgs["variances"] = formatted - popFn() - }() - } - stackIndex := c.getVarianceStackIndex(symbol) - if stackIndex < 0 { - saveResolutionStart := c.resolutionStart - if len(c.varianceStack) == 0 { - c.resolutionStart = len(c.typeResolutions) - } - c.varianceStack = append(c.varianceStack, VarianceStackEntry{symbol, typeParameters}) - variances := make([]VarianceFlags, len(typeParameters)) - for i, tp := range typeParameters { - modifiers := c.getTypeParameterModifiers(tp) - var variance VarianceFlags - switch { - case modifiers&ast.ModifierFlagsOut != 0: - if modifiers&ast.ModifierFlagsIn != 0 { - variance = VarianceFlagsInvariant - } else { - variance = VarianceFlagsCovariant - } - case modifiers&ast.ModifierFlagsIn != 0: - variance = VarianceFlagsContravariant - default: - saveReliabilityFlags := c.reliabilityFlags - c.reliabilityFlags = 0 - // We first compare instantiations where the type parameter is replaced with - // marker types that have a known subtype relationship. From this we can infer - // invariance, covariance, contravariance or bivariance. - typeWithSuper := c.createMarkerType(symbol, tp, c.markerSuperType) - typeWithSub := c.createMarkerType(symbol, tp, c.markerSubType) - variance = core.IfElse(c.isTypeAssignableTo(typeWithSub, typeWithSuper), VarianceFlagsCovariant, 0) | - core.IfElse(c.isTypeAssignableTo(typeWithSuper, typeWithSub), VarianceFlagsContravariant, 0) - // If the instantiations appear to be related bivariantly it may be because the - // type parameter is independent (i.e. it isn't witnessed anywhere in the generic - // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. - if variance == VarianceFlagsBivariant && c.isTypeAssignableTo(c.createMarkerType(symbol, tp, c.markerOtherType), typeWithSuper) { - variance = VarianceFlagsIndependent - } - if c.reliabilityFlags&RelationComparisonResultReportsUnmeasurable != 0 { - variance |= VarianceFlagsUnmeasurable - } - if c.reliabilityFlags&RelationComparisonResultReportsUnreliable != 0 { - variance |= VarianceFlagsUnreliable - } - c.reliabilityFlags = saveReliabilityFlags - } - // If variance computation was restarted due to a circularity we may have already - // computed variances for this generic type. If so, we exit early. - if len(links.variances) != 0 { - break - } - variances[i] = variance - } - // Store the results unless a restarted computation has already stored them. - if len(links.variances) == 0 { - links.variances = variances - } - c.varianceStack = c.varianceStack[:len(c.varianceStack)-1] - if len(c.varianceStack) == 0 { - c.resolutionStart = saveResolutionStart - } - } else { - // We've detected a circularity. Since we may compute different variances depending on where - // we enter a circularity, we find the generic type with the "smallest" symbol in the circular - // region of the variance stack and restart the computation from there if necessary. This - // ensures stable results for circular generic types. - minIndex := stackIndex - for i := stackIndex + 1; i < len(c.varianceStack); i++ { - if c.compareSymbols(c.varianceStack[i].symbol, c.varianceStack[minIndex].symbol) < 0 { - minIndex = i - } - } - if minIndex > stackIndex { - saveVarianceStack := c.varianceStack - c.varianceStack = nil - c.getVariancesWorker(saveVarianceStack[minIndex].symbol, saveVarianceStack[minIndex].typeParameters) - c.varianceStack = saveVarianceStack + if variances := links.variances.get(c); variances != nil { + return variances + } + if stackIndex := c.getVarianceStackIndex(symbol); stackIndex >= 0 { + return c.getRecursiveVariances(stackIndex) + } + var traceArgs map[string]any + if tr := c.tracer; tr != nil { + traceArgs = map[string]any{"arity": len(typeParameters), "id": c.getDeclaredTypeOfSymbol(symbol).id} + popFn := tr.Push(tracing.PhaseCheckTypes, "getVariancesWorker", traceArgs, true) + defer func() { + variances := links.variances.get(c) + formatted := make([]string, len(variances)) + for i, v := range variances { + formatted[i] = v.String() + } + traceArgs["variances"] = formatted + popFn() + }() + } + links.variances.begin(c) + saveResolutionStart := c.resolutionStart + if len(c.varianceStack) == 0 { + c.resolutionStart = len(c.resolutions) + } + index := len(c.varianceStack) + c.varianceStack = append(c.varianceStack, VarianceStackEntry{symbol: symbol, resolution: len(c.resolutions), low: index, min: symbol, minTypeParameters: typeParameters, discardPoint: c.discardPoint(), provisionalStart: len(c.varianceProvisional)}) + c.beginTypeResolution(symbol, TypeSystemPropertyNameVariances, &links.variances.memoHeader) + variances := c.measureVariances(symbol, typeParameters, links) + e := c.varianceStack[index] + if index == 0 { + // A superseded measurement found a smaller type in its circularity, + // from which it is measured again below. + debug.Assert(!c.varianceSuperseded || e.min != symbol && !c.isYieldingTo(e.min, TypeSystemPropertyNameVariances), "superseded variance measurement not measured again") + c.varianceSuperseded = false + } + if e.low == index && e.min != symbol && !c.isYieldingTo(e.min, TypeSystemPropertyNameVariances) { + // The measurement entered a circularity, of which this type is the + // first entered, at a type other than the canonical entry. What it + // measured depends on where the circularity was entered, so it is + // discarded, and the circularity measured again from the canonical + // entry, as if that type had been measured first, with nothing in + // progress visible. That measures this type as it reaches it. + c.discardVariancesSince(e) + saveVarianceStack, saveCycleRoot, saveProvisional := c.varianceStack, c.varianceCycleRoot, c.varianceProvisional + c.varianceStack, c.varianceCycleRoot, c.varianceProvisional = nil, 0, nil + c.beginCycleYield(e.min, TypeSystemPropertyNameVariances, -1, len(c.resolutions)) + c.getVariancesWorker(e.min, e.minTypeParameters) + c.endCycleYield() + c.varianceStack, c.varianceCycleRoot, c.varianceProvisional = saveVarianceStack, saveCycleRoot, saveProvisional + variances = links.variances.get(c) + if len(variances) == 0 { + c.varianceStack[index].remeasuring = true + variances = c.measureVariances(symbol, typeParameters, links) + } + } else if restarted := links.variances.get(c); len(restarted) != 0 { + // A measurement from the canonical entry of a circularity measured + // this type while its own measurement was in progress, hidden. + variances = restarted + } + links.variances.set(c, variances) + if c.varianceCycleRoot == index+1 { + c.varianceCycleRoot = 0 + c.trimDiscardLog() + c.resumeChecking() + } + c.varianceStack = c.varianceStack[:index] + if index != 0 && e.low < index { + // This type is in a circularity with the measurement that reached it, + // so its variances depend on those of a measurement in progress, and + // are as much a guess as those until it finishes. + c.varianceProvisional = append(c.varianceProvisional, symbol) + parent := &c.varianceStack[index-1] + parent.low = min(parent.low, e.low) + if c.compareSymbols(e.min, parent.min) < 0 { + parent.min, parent.minTypeParameters = e.min, e.minTypeParameters + } + } else { + clear(c.varianceProvisional[e.provisionalStart:]) + c.varianceProvisional = c.varianceProvisional[:e.provisionalStart] + } + c.popTypeResolution() + if len(c.varianceStack) == 0 { + c.resolutionStart = saveResolutionStart + } + c.endMemo() + return variances +} + +// getRecursiveVariances handles a measurement needing the variances of the type at the given +// index of the variance stack, whose measurement is in progress. Occurrences of type parameters +// nested in such recursive references are not measured: the variances are an empty slice, which +// comparisons treat as unknown. Since the variances measured then depend on where the +// circularity was entered, each circularity (a strongly connected component of types whose +// measurements need each other's variances) is measured from a canonical entry, the type with +// the "smallest" symbol, as if that type had been measured first: the measurement of the type +// at which it was entered finds the canonical entry once it finishes (see getVariancesWorker). +func (c *Checker) getRecursiveVariances(stackIndex int) []VarianceFlags { + top := &c.varianceStack[len(c.varianceStack)-1] + top.low = min(top.low, stackIndex) + // The variances are incomplete. Only comparisons and inferences read + // variances, and a comparison observing them is not cached. What else + // is computed from them is kept if the measurement is not discarded: + // the outermost measurement hides what is below it, so what is computed + // inside it is the same whatever led to it, and computing any of it + // first from scratch reaches the measurement and computes it again + // inside, the same way. What is cached from now on until the + // measurement re-entered finishes is logged, so that it can be discarded + // if the measurement is. + if c.varianceCycleRoot == 0 { + c.recordFromCheck() + c.varianceCycleRoot = stackIndex + 1 + } else { + c.varianceCycleRoot = min(c.varianceCycleRoot, stackIndex+1) + } + bottom := &c.varianceStack[0] + c.reliabilityFlags |= RelationComparisonResultReportsIncomplete + if stackIndex == 0 && !c.varianceSuperseded && !c.varianceStack[0].remeasuring { + // The circularity includes the measurement at the bottom of the stack, + // so that is where it was entered. If it has a smaller type, what the + // measurement measures is discarded once it finishes, so it stops + // measuring now (see Relater.recursiveTypeRelatedTo). + for _, f := range c.varianceStack[1:] { + if c.compareSymbols(f.min, bottom.symbol) < 0 && !c.isYieldingTo(f.min, TypeSystemPropertyNameVariances) { + c.varianceSuperseded = true + break } - // Store an empty slice to mark that we can't compute variances for this type. We treat type - // parameters as co-variant in this case. - if len(links.variances) == 0 { - links.variances = []VarianceFlags{} + } + } + return []VarianceFlags{} +} + +// discardVariancesSince discards the results cached since e began, and all +// the effects the measurement recorded. +func (c *Checker) discardVariancesSince(e VarianceStackEntry) { + clear(c.varianceProvisional[e.provisionalStart:]) + c.varianceProvisional = c.varianceProvisional[:e.provisionalStart] + c.discardTo(e.discardPoint) + if f := c.resolutions[e.resolution-1].effects; f != nil { + f.truncate(0, 0) + } +} + +func (c *Checker) measureVariances(symbol *ast.Symbol, typeParameters []*Type, links *VarianceLinks) []VarianceFlags { + variances := make([]VarianceFlags, len(typeParameters)) + for i, tp := range typeParameters { + modifiers := c.getTypeParameterModifiers(tp) + var variance VarianceFlags + switch { + case modifiers&ast.ModifierFlagsOut != 0: + if modifiers&ast.ModifierFlagsIn != 0 { + variance = VarianceFlagsInvariant + } else { + variance = VarianceFlagsCovariant } + case modifiers&ast.ModifierFlagsIn != 0: + variance = VarianceFlagsContravariant + default: + saveReliabilityFlags := c.reliabilityFlags + c.reliabilityFlags = 0 + // We first compare instantiations where the type parameter is replaced with + // marker types that have a known subtype relationship. From this we can infer + // invariance, covariance, contravariance or bivariance. + typeWithSuper := c.createMarkerType(symbol, tp, c.markerSuperType) + typeWithSub := c.createMarkerType(symbol, tp, c.markerSubType) + variance = core.IfElse(c.isTypeAssignableTo(typeWithSub, typeWithSuper), VarianceFlagsCovariant, 0) | + core.IfElse(c.isTypeAssignableTo(typeWithSuper, typeWithSub), VarianceFlagsContravariant, 0) + // If the instantiations appear to be related bivariantly it may be because the + // type parameter is independent (i.e. it isn't witnessed anywhere in the generic + // type). To determine this we compare instantiations where the type parameter is + // replaced with marker types that are known to be unrelated. + if variance == VarianceFlagsBivariant && c.isTypeAssignableTo(c.createMarkerType(symbol, tp, c.markerOtherType), typeWithSuper) { + variance = VarianceFlagsIndependent + } + if c.reliabilityFlags&RelationComparisonResultReportsUnmeasurable != 0 { + variance |= VarianceFlagsUnmeasurable + } + if c.reliabilityFlags&RelationComparisonResultReportsUnreliable != 0 { + variance |= VarianceFlagsUnreliable + } + c.reliabilityFlags = saveReliabilityFlags + } + // If variance computation was restarted due to a circularity we may have already + // computed variances for this generic type. If so, we exit early. + if len(links.variances.get(c)) != 0 { + break } + variances[i] = variance } - return links.variances + return variances +} + +// isGuessedVariance reports whether the variances of symbol are a guess: +// those of a measurement in progress, or measured in a circularity with one. +func (c *Checker) isGuessedVariance(symbol *ast.Symbol) bool { + return c.getVarianceStackIndex(symbol) >= 0 || slices.Contains(c.varianceProvisional, symbol) } +// getVarianceStackIndex returns the index in the variance stack of the +// measurement of the variances of symbol in progress, or -1. A measurement +// hidden by a cycle yield is not in progress. func (c *Checker) getVarianceStackIndex(symbol *ast.Symbol) int { - for i, entry := range c.varianceStack { - if entry.symbol == symbol { - return i - } + if len(c.varianceStack) == 0 { + return -1 + } + r := c.resolvingIndex(c.varianceLinks.Get(symbol).variances.key()) + if r == 0 { + return -1 + } + i, found := slices.BinarySearchFunc(c.varianceStack, r, func(e VarianceStackEntry, r int) int { return e.resolution - r }) + if !found { + return -1 } - return -1 + return i } func (c *Checker) createMarkerType(symbol *ast.Symbol, source *Type, target *Type) *Type { @@ -1555,16 +1741,67 @@ func (c *Checker) createMarkerType(symbol *ast.Symbol, source *Type, target *Typ } var result *Type if symbol.Flags()&ast.SymbolFlagsTypeAlias != 0 { - result = c.getTypeAliasInstantiation(symbol, c.instantiateTypes(c.typeAliasLinks.Get(symbol).typeParameters, mapper), nil) + result = c.getTypeAliasInstantiation(symbol, c.instantiateTypes(c.getTypeParametersOfTypeAlias(symbol), mapper), nil) } else { result = c.createTypeReference(t, c.instantiateTypes(t.AsInterfaceType().TypeParameters(), mapper)) } - c.markerTypes.Add(result) return result } +// isMarkerType reports whether t is a marker type of the innermost variance +// computation or variance annotation check in progress: an instantiation of +// the generic type or type alias being measured with one of its type +// parameters replaced by a variance marker. Instantiations of other types with +// markers, such as those nested in the type being measured, are not marker +// types, whether or not their own variances have been measured before. Nor +// are those of measurements further down the variance stack: a reference to +// them must get their variances, so that the circularity is found and +// measured from its canonical entry (see getRecursiveVariances). func (c *Checker) isMarkerType(t *Type) bool { - return c.markerTypes.Has(t) + if len(c.varianceStack) == 0 && c.varianceTypeParameter == nil { + return false + } + if t.alias != nil && t.alias.symbol.Flags()&ast.SymbolFlagsTypeAlias != 0 && + c.isMarkerInstantiation(t.alias.symbol, c.typeAliasLinks.Get(t.alias.symbol).typeParameters, t.alias.typeArguments) { + return true + } + if t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil { + target := t.Target() + if target != nil && target != t && target.objectFlags&ObjectFlagsClassOrInterface != 0 { + return c.isMarkerInstantiation(target.symbol, target.AsInterfaceType().TypeParameters(), t.AsTypeReference().resolvedTypeArguments.peek()) + } + } + return false +} + +func (c *Checker) isMarkerInstantiation(symbol *ast.Symbol, typeParameters []*Type, typeArguments []*Type) bool { + if len(typeParameters) == 0 || len(typeParameters) != len(typeArguments) { + return false + } + replaced := -1 + for i, tp := range typeParameters { + if typeArguments[i] == tp { + continue + } + if replaced >= 0 || !c.isVarianceMarker(typeArguments[i]) { + return false + } + replaced = i + } + if replaced < 0 { + return false + } + switch typeArguments[replaced] { + case c.markerSuperTypeForCheck, c.markerSubTypeForCheck: + return typeParameters[replaced] == c.varianceTypeParameter + default: + return len(c.varianceStack) != 0 && c.varianceStack[len(c.varianceStack)-1].symbol == symbol + } +} + +func (c *Checker) isVarianceMarker(t *Type) bool { + return t == c.markerSuperType || t == c.markerSubType || t == c.markerOtherType || + t == c.markerSuperTypeForCheck || t == c.markerSubTypeForCheck } func (c *Checker) getTypeParameterModifiers(tp *Type) ast.ModifierFlags { @@ -1843,9 +2080,23 @@ func (c *Checker) getMinArgumentCount(signature *Signature) int { } func (c *Checker) getMinArgumentCountEx(signature *Signature, flags MinArgumentCountFlags) int { + voidIsNonOptional := flags & MinArgumentCountFlagsVoidIsNonOptional + if voidIsNonOptional != 0 { + return c.getMinArgumentCountUncached(signature, flags) + } + if count := signature.resolvedMinArgumentCount.get(c); count != -1 { + return int(count) + } + signature.resolvedMinArgumentCount.begin(c) + result := c.getMinArgumentCountUncached(signature, flags) + c.endMemo() + return result +} + +func (c *Checker) getMinArgumentCountUncached(signature *Signature, flags MinArgumentCountFlags) int { strongArityForUntypedJS := flags & MinArgumentCountFlagsStrongArityForUntypedJS voidIsNonOptional := flags & MinArgumentCountFlagsVoidIsNonOptional - if voidIsNonOptional != 0 || signature.resolvedMinArgumentCount == -1 { + { minArgumentCount := -1 if signatureHasRestParameter(signature) { restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) @@ -1878,9 +2129,9 @@ func (c *Checker) getMinArgumentCountEx(signature *Signature, flags MinArgumentC } minArgumentCount = i } - signature.resolvedMinArgumentCount = int32(minArgumentCount) + signature.resolvedMinArgumentCount.set(c, int32(minArgumentCount)) + return minArgumentCount } - return int(signature.resolvedMinArgumentCount) } func (c *Checker) hasEffectiveRestParameter(signature *Signature) bool { @@ -2151,6 +2402,22 @@ func (c *Checker) getTupleElementLabelFromBindingElement(node *ast.Node, index i } func (c *Checker) getTypePredicateOfSignature(sig *Signature) *TypePredicate { + key := memoKeyOf(sig, memoTypePredicate) + if sig.getResolvedTypePredicate(c) != nil { + c.observeFlagMemo(key) + } else { + c.beginMemo(key) + c.resolveTypePredicateOfSignature(sig) + c.endMemo() + } + predicate := sig.getResolvedTypePredicate(c) + if predicate == c.noTypePredicate { + return nil + } + return predicate +} + +func (c *Checker) resolveTypePredicateOfSignature(sig *Signature) { if sig.getResolvedTypePredicate(c) == nil { switch { case sig.target != nil: @@ -2168,7 +2435,7 @@ func (c *Checker) getTypePredicateOfSignature(sig *Signature) *TypePredicate { if ast.IsTypePredicateNode(typeNode) { sig.setResolvedTypePredicate(c, c.createTypePredicateFromTypePredicateNode(typeNode, sig)) } - case ast.IsFunctionLikeDeclaration(sig.declaration) && (sig.resolvedReturnType == nil || sig.resolvedReturnType.flags&TypeFlagsBoolean != 0) && c.getParameterCount(sig) > 0: + case ast.IsFunctionLikeDeclaration(sig.declaration) && (sig.resolvedReturnType.get(c) == nil || sig.resolvedReturnType.get(c).flags&TypeFlagsBoolean != 0) && c.getParameterCount(sig) > 0: sig.setResolvedTypePredicate(c, c.noTypePredicate) // avoid infinite loop sig.setResolvedTypePredicate(c, c.getTypePredicateFromBody(sig.declaration)) } @@ -2178,11 +2445,6 @@ func (c *Checker) getTypePredicateOfSignature(sig *Signature) *TypePredicate { sig.setResolvedTypePredicate(c, c.noTypePredicate) } } - predicate := sig.getResolvedTypePredicate(c) - if predicate == c.noTypePredicate { - return nil - } - return predicate } func (c *Checker) getUnionOrIntersectionTypePredicate(signatures []*Signature, isUnion bool) *TypePredicate { @@ -2251,7 +2513,18 @@ func (c *Checker) isResolvingReturnTypeOfSignature(signature *Signature) bool { if signature.getComposite() != nil && core.Some(signature.getComposite().signatures, c.isResolvingReturnTypeOfSignature) { return true } - return signature.resolvedReturnType == nil && c.findResolutionCycleStartIndex(signature, TypeSystemPropertyNameResolvedReturnType) >= 0 + if signature.resolvedReturnType.get(c) != nil { + return false + } + start := c.findResolutionCycleStartIndex(signature, TypeSystemPropertyNameResolvedReturnType) + if start < 0 { + return false + } + c.markTypeResolutionCircular(start) + // Computed from the first member of the cycle, the return type may be + // available after all. + c.yieldObservedCycle(start) + return signature.resolvedReturnType.get(c) == nil } func (c *Checker) findMatchingSignatures(signatureLists [][]*Signature, signature *Signature, listIndex int) []*Signature { @@ -2693,8 +2966,18 @@ type Relater struct { // them by key. maybeKeys []maybeKey maybeKeysSet map[CacheHashKey]int - sourceStack []*Type - targetStack []*Type + // The outermost comparison and its relation key. + topSource, topTarget *Type + topKey CacheHashKey + topConstrained bool + topEntry RelationComparisonResult // The result cached for topKey when the comparison began + topEntryValid bool // Whether topEntry is unused, and so still the cached result + topHasKey bool // Whether topKey and topConstrained are known + topEntryRead bool // Whether the caller read topEntry, when the relation had topEntryWrites writes + topEntryWrites uint32 + topBegun bool // Whether the comparison began its computation; see beginTop + sourceStack []*Type + targetStack []*Type // sourceDepths and targetDepths are the depths of the comparisons that // pushed each entry of sourceStack and targetStack. sourceDepths []int @@ -2727,6 +3010,37 @@ type maybeKey struct { truncated bool } +// beginTop makes the outermost comparison a computation of its own, so that +// reusing any result it caches accounts for the effects of the whole +// comparison. Errors are reported after it, since they belong to the caller +// rather than the result. Only recursiveTypeRelatedTo caches results, so the +// comparison begins on its first call there, and most comparisons, which +// finish before, need neither the computation nor the relation key. What the +// comparison did before then cannot affect a result it caches other than +// through the types it compares. beginTop returns false if the comparison +// overflowed before, and would again. +func (r *Relater) beginTop() bool { + r.topBegun = true + c := r.c + if !r.topHasKey { + r.topKey, r.topConstrained = c.getRelationKey(r.topSource, r.topTarget, IntersectionStateNone, r.relation == c.identityRelation, false /*ignoreConstraints*/) + r.topHasKey = true + } + // A comparison made before now may have cached a result for the key, + // unless nothing was cached since the caller read it. + if !r.topEntryRead || r.topEntryWrites != r.relation.results.writes { + r.topEntry = r.relation.get(c, r.topKey) + } + r.topEntryValid = true + if r.topEntry&RelationComparisonResultOverflow != 0 { + r.topBegun = false + r.overflow = true + return false + } + r.relation.results.begin(c, r.topKey) + return true +} + func (c *Checker) getRelater() *Relater { r := c.freeRelater if r == nil { @@ -2737,17 +3051,25 @@ func (c *Checker) getRelater() *Relater { } func (c *Checker) putRelater(r *Relater) { - clear(r.maybeKeysSet) - *r = Relater{ - c: c, - maybeKeys: r.maybeKeys[:0], - maybeKeysSet: r.maybeKeysSet, - sourceStack: r.sourceStack[:0], - targetStack: r.targetStack[:0], - sourceDepths: r.sourceDepths[:0], - targetDepths: r.targetDepths[:0], - next: c.freeRelater, - } + if len(r.maybeKeysSet) != 0 { + clear(r.maybeKeysSet) + } + // Reset field by field, which is cheaper than assigning a whole Relater. + r.relation = nil + r.errorNode = nil + r.errorChain = nil + r.relatedInfo = nil + r.maybeKeys = r.maybeKeys[:0] + r.sourceStack, r.targetStack = r.sourceStack[:0], r.targetStack[:0] + r.sourceDepths, r.targetDepths = r.sourceDepths[:0], r.targetDepths[:0] + r.depth, r.low, r.truncated = 0, 0, false + r.topSource, r.topTarget = nil, nil + r.topKey, r.topConstrained = CacheHashKey{}, false + r.topEntry, r.topEntryValid, r.topHasKey, r.topBegun, r.topEntryRead = RelationComparisonResultNone, false, false, false, false + r.expandingFlags, r.expandingSourceDepth, r.expandingTargetDepth = ExpandingFlagsNone, 0, 0 + r.overflow = false + r.relationCount = 0 + r.next = c.freeRelater c.freeRelater = r } @@ -3249,16 +3571,47 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError // important to have well-defined behavior even in cases that shouldn't normally occur. return TernaryFalse } - 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 { + if r.c.varianceSuperseded && r.c.isMarkerType(r.topSource) { + // The measurement at the bottom of the variance stack is discarded + // once it finishes (see getRecursiveVariances), so the comparisons + // measuring variances stop here, as if reading its incomplete + // variances. Other comparisons go on: what is computed from their + // results may be kept. + r.c.reliabilityFlags |= RelationComparisonResultReportsIncomplete + return TernaryFalse + } + if !r.topBegun && !r.beginTop() { + return TernaryFalse + } + var id CacheHashKey + var constrained bool + var entry RelationComparisonResult + if source == r.topSource && target == r.topTarget && intersectionState == IntersectionStateNone { + id, constrained = r.topKey, r.topConstrained + // The comparison read the cached result on starting, and nothing + // caches one for it before its first visit to its own pair. + if r.topEntryValid { + r.topEntryValid = false + entry = r.topEntry + } else { + entry = r.relation.get(r.c, id) + } + } else { + id, constrained = r.c.getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, false /*ignoreConstraints*/) + entry = r.relation.get(r.c, id) + } + if entry != RelationComparisonResultNone { + if entry&RelationComparisonResultOverflow != 0 { + // Computing the comparison overflowed, so computing this one, + // which needs it, overflows too. + r.overflow = true + return TernaryFalse + } + if reportErrors && entry&RelationComparisonResultFailed != 0 { // We are elaborating errors and the cached result is a failure not due to a comparison overflow, // so we will do the comparison again to generate an error message. } else { r.c.reliabilityFlags |= entry & (RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable) - if reportErrors && entry&RelationComparisonResultOverflow != 0 { - r.reportError(diagnostics.Excessive_complexity_comparing_types_0_and_1, r.c.TypeToString(source), r.c.TypeToString(target)) - } if entry&RelationComparisonResultSucceeded != 0 { return TernaryTrue } @@ -3277,13 +3630,13 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError // type parameters. For such keys we also check against the key we would have gotten if all type parameters // were unconstrained. if constrained { - broadestEquivalentId, _ := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, true /*ignoreConstraints*/) + broadestEquivalentId, _ := r.c.getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, true /*ignoreConstraints*/) if i, ok := r.maybeKeysSet[broadestEquivalentId]; ok { return r.assume(i) } // What relates with unconstrained type parameters also relates with // constrained ones. - if entry := r.relation.get(broadestEquivalentId); entry&RelationComparisonResultSucceeded != 0 { + if entry := r.relation.get(r.c, broadestEquivalentId); entry&RelationComparisonResultSucceeded != 0 { r.c.reliabilityFlags |= entry & (RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable) return TernaryTrue } @@ -3346,9 +3699,15 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError if tr := r.c.tracer; tr != nil { defer tr.Push(tracing.PhaseCheckTypes, "structuredTypeRelatedTo", map[string]any{"sourceId": source.id, "targetId": target.id}, false)() } + // The result is cached, so instantiations it needs count against + // the depth limit from here, not from wherever it is first compared + // (see "Instantiation limits" in memo.go). + r.c.beginResolution(nil, 0) result = r.structuredTypeRelatedTo(source, target, reportErrors, intersectionState) + r.c.endResolution() } - propagatingVarianceFlags := r.c.reliabilityFlags + propagatingVarianceFlags := r.c.reliabilityFlags & RelationComparisonResultReportsMask + incomplete := r.c.reliabilityFlags&RelationComparisonResultReportsIncomplete != 0 r.c.reliabilityFlags |= saveReliabilityFlags if recursionFlags&RecursionFlagsSource != 0 { r.sourceStack = r.sourceStack[:len(r.sourceStack)-1] @@ -3363,10 +3722,27 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError low, truncated := r.low, r.truncated r.low, r.truncated = saveLow, saveTruncated switch { + case r.overflow: + // The comparison was cut short, so its result and those of the + // comparisons that completed assuming it are not what computing them + // from scratch would give. Only the overflow is cached. + r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) + result = TernaryFalse + case incomplete && low >= depth: + // The result observed incomplete variances, so it is not what + // comparing from scratch would give (see getRecursiveVariances). + r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) + if result == TernaryMaybe { + result = TernaryTrue + } + case incomplete: + // As above, once the outermost comparison this one assumed completes. + r.low = min(r.low, low) + r.truncated = r.truncated || truncated case result == TernaryFalse: // A false result goes straight into global cache (when something is false under // assumptions it will also be false without assumptions) - r.relation.set(id, RelationComparisonResultFailed|propagatingVarianceFlags) + r.relation.set(r.c, id, RelationComparisonResultFailed|propagatingVarianceFlags) r.relationCount-- r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) case low >= depth: @@ -3379,7 +3755,7 @@ func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportError r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) case truncated: r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) - r.relation.set(id, RelationComparisonResultSucceeded|propagatingVarianceFlags) + r.relation.set(r.c, id, RelationComparisonResultSucceeded|propagatingVarianceFlags) r.relationCount-- default: r.resetMaybeStack(maybeStart, propagatingVarianceFlags, true) @@ -3428,7 +3804,7 @@ func (r *Relater) resetMaybeStack(maybeStart int, propagatingVarianceFlags Relat key := r.maybeKeys[i].key delete(r.maybeKeysSet, key) if markAllAsSucceeded { - r.relation.set(key, RelationComparisonResultSucceeded|propagatingVarianceFlags) + r.relation.set(r.c, key, RelationComparisonResultSucceeded|propagatingVarianceFlags) r.relationCount-- } } @@ -3521,10 +3897,24 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo var varianceCheckFailed bool var originalErrorChain *ErrorChain saveErrorState := r.getErrorState() - relateVariances := func(sourceTypeArguments []*Type, targetTypeArguments []*Type, variances []VarianceFlags, intersectionState IntersectionState) (Ternary, bool) { + relateVariances := func(sourceTypeArguments []*Type, targetTypeArguments []*Type, variances []VarianceFlags, guessed bool, intersectionState IntersectionState) (Ternary, bool) { if result = r.typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, intersectionState); result != TernaryFalse { return result, true } + if guessed { + // The variances are a guess for a generic type whose variances are being + // measured, or were measured in a circularity with a measurement still in + // progress (see isGuessedVariance). There is no structural fallback for + // them, so that a stricter guess never makes a comparison succeed, and the + // measurement depends on the guess monotonically. The measurement still + // learns that it observed unreliable or unmeasurable variances. + for i, v := range variances { + if v&VarianceFlagsUnmeasurable != 0 && i < len(sourceTypeArguments) { + r.c.instantiateType(sourceTypeArguments[i], r.c.reportUnmeasurableMapper) + } + } + return TernaryFalse, true + } if core.Some(variances, func(v VarianceFlags) bool { return v&VarianceFlagsAllowsStructuralFallback != 0 }) { // If some type parameter was `Unmeasurable` or `Unreliable`, and we couldn't pass by assuming it was identical, then we // have to allow a structural fallback check @@ -3658,7 +4048,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo nodeIsInJsFile := ast.IsInJSFile(source.alias.symbol.ValueDeclaration()) sourceTypes := r.c.fillMissingTypeArguments(source.alias.typeArguments, params, minParams, nodeIsInJsFile) targetTypes := r.c.fillMissingTypeArguments(target.alias.typeArguments, params, minParams, nodeIsInJsFile) - varianceResult, ok := relateVariances(sourceTypes, targetTypes, variances, intersectionState) + varianceResult, ok := relateVariances(sourceTypes, targetTypes, variances, r.c.isGuessedVariance(source.alias.symbol), intersectionState) if ok { return varianceResult } @@ -4088,13 +4478,10 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo // type references (which are intended by be compared structurally). Obtain the variance // information for the type parameters and relate the type arguments accordingly. variances := r.c.getVariances(source.Target()) - // We return Ternary.Maybe for a recursive invocation of getVariances (signaled by emptyArray). This - // effectively means we measure variance only from type parameter occurrences that aren't nested in - // recursive instantiations of the generic type. if len(variances) == 0 { return TernaryUnknown } - varianceResult, ok := relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, intersectionState) + varianceResult, ok := relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, r.c.isGuessedVariance(source.Target().symbol), intersectionState) if ok { return varianceResult } @@ -5000,7 +5387,7 @@ func (r *Relater) reportErrorResults(originalSource *Type, originalTarget *Type, r.reportRelationError(headMessage, source, target) if source.flags&TypeFlagsTypeParameter != 0 && source.symbol != nil && len(source.symbol.Declarations()) != 0 && r.c.getConstraintOfType(source) == nil { syntheticParam := r.c.cloneTypeParameter(source) - syntheticParam.AsTypeParameter().constraint = r.c.instantiateType(target, newSimpleTypeMapper(source, syntheticParam)) + syntheticParam.AsTypeParameter().constraint.intern(r.c.instantiateType(target, newSimpleTypeMapper(source, syntheticParam))) if r.c.hasNonCircularBaseConstraint(syntheticParam) { targetConstraintString := r.c.TypeToString(target) r.relatedInfo = append(r.relatedInfo, NewDiagnosticForNode(source.symbol.Declarations()[0], diagnostics.This_type_parameter_might_need_an_extends_0_constraint, targetConstraintString)) @@ -5028,7 +5415,7 @@ func (r *Relater) reportRelationError(message *diagnostics.Message, source *Type if targetFlags&TypeFlagsTypeParameter != 0 && target != r.c.markerSuperTypeForCheck && target != r.c.markerSubTypeForCheck { constraint := r.c.getBaseConstraintOfType(target) switch { - case IsDistributedTypeParameter(target) && r.c.isTypeAssignableTo(generalizedSource, target.AsTypeParameter().constraint): + case IsDistributedTypeParameter(target) && r.c.isTypeAssignableTo(generalizedSource, target.AsTypeParameter().constraint.get(r.c)): r.reportError(diagnostics.X_0_is_only_assignable_to_the_non_distributed_1_but_1_has_been_distributed_here, generalizedSourceType, targetType) case constraint != nil && r.c.isTypeAssignableTo(generalizedSource, constraint): r.reportError(diagnostics.X_0_is_assignable_to_the_constraint_of_type_1_but_1_could_be_instantiated_with_a_different_subtype_of_constraint_2, generalizedSourceType, targetType, r.c.TypeToString(constraint)) diff --git a/tsc/internal/checker/resolutions.go b/tsc/internal/checker/resolutions.go new file mode 100644 index 0000000000000..f5abfff3e0fcb --- /dev/null +++ b/tsc/internal/checker/resolutions.go @@ -0,0 +1,310 @@ +package checker + +import ( + "slices" + + "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/debug" +) + +// A resolution is a computation in progress: a memoized computation, a cached +// comparison, or a checked node. The innermost is the last in +// Checker.resolutions, above a root resolution for work outside any of them. +type resolution struct { + // The result computed is the memo identified by key, or the one whose + // memo state source creates for index, if either is given. + key memoKey + source memoStateSource + index int32 + // Length of the instantiation stack from which the depth limit counts: + // that on entry for a computation, or that of the enclosing resolution + // for a node. + depthStart int32 + serial uint32 // Identifies the resolution among all entered + + // A query records the effects of a memoized computation, for replay by + // reuse of its result; see memo.go. + memo bool + resolving bool // The resolution is the one in Checker.resolving for key + fromCheck bool // The query began in a check; see recordFromCheck + // The computation read the incomplete result of a computation in + // progress, or was started by one that did; see effectsToDrop. + readIncomplete bool + // The computation reported an error located at the current node; see + // effectsToDrop. + reportsAtCurrentNode bool + // The structured members the outermost resolution of key computes are + // available before it completes; see resolveStructuredTypeMembersUncached. + membersPublished bool + prevMemo int32 // Checker.memoTop on entry + // Index in Checker.deferredAttach of the first result cached by the + // computation before it had effects; see attachState. + attachStart int32 + result *memoState // Memo state of the result computed + effects *memoEffects // Allocated on first use + entry *ast.Node // Current node when the computation started + // The symbol whose member table this memo computes, if any. Keeping it + // directly lets canonical cycle entry compare declarations without + // recovering a pointer from the packed memo key. + memberSymbol *ast.Symbol + + // A type resolution is a computation that re-entering detects as a cycle; + // see beginTypeResolution. Its other fields are those of the resolution + // below it, whose work it shares. + typeResolution TypeResolution + isTypeResolution bool + prevTypeResolution int32 // Checker.typeResolutionTop on entry + prevObservedHead int32 // Checker.observedHead on entry +} + +// A memoStateSource creates the memo state of a computation in progress on +// demand, since few computations do work worth storing. +type memoStateSource interface { + stateAt(i int) *memoState + peekStateAt(i int) *memoState + popState(i int) +} + +func (c *Checker) initResolutions() { + c.resolutions = []resolution{{}} + c.syncObserving() +} + +// pushResolution pushes a resolution with every field zero and returns it. +// Popping a resolution zeroes it, so space reused above the stack needs no +// zeroing, and the caller sets only the fields it needs in place rather than +// copying in a whole resolution. +func (c *Checker) pushResolution() *resolution { + n := len(c.resolutions) + if n == 1 { + // An outermost computation, such as a query, counts its own + // instantiations; see "Instantiation limits" in memo.go. + c.instantiationCount = 0 + } + if n == cap(c.resolutions) { + c.resolutions = append(c.resolutions, resolution{}) + } else { + c.resolutions = c.resolutions[:n+1] + } + f := &c.resolutions[n] + // What a computation started by one that read an incomplete result + // computes may be derived from it too. + f.readIncomplete = n != 0 && c.resolutions[n-1].readIncomplete + return f +} + +func (c *Checker) currentResolution() *resolution { + return &c.resolutions[len(c.resolutions)-1] +} + +// beginResolution starts a resolution for a computation, which also starts the +// depth limit of instantiations (see "Instantiation limits" in memo.go). +func (c *Checker) beginResolution(source memoStateSource, index int) { + c.appendResolution(0, source, index, int32(len(c.instantiationStack))) +} + +func (c *Checker) appendResolution(key memoKey, source memoStateSource, index int, depthBase int32) { + c.resolutionSerials++ + f := c.pushResolution() + f.key = key + f.source = source + f.index = int32(index) + f.depthStart = depthBase + f.serial = c.resolutionSerials +} + +// endResolution finishes the current resolution. +func (c *Checker) endResolution() { + n := len(c.resolutions) - 1 + f := &c.resolutions[n] + debug.Assert(!f.isTypeResolution, "resolution left on the stack") + c.endResolving(f) + fromCheck := f.fromCheck + if f.memo { + clear(c.deferredAttach[f.attachStart:]) + c.deferredAttach = c.deferredAttach[:f.attachStart] + } + if f.source != nil { + f.source.popState(int(f.index)) + } + c.dropResolution(n) + if fromCheck { + c.endQueryFromCheck() + } +} + +// forgetResult makes the current resolution compute no result, for a +// computation that did not cache its result. +func (c *Checker) forgetResult() { + f := c.currentResolution() + if f.source != nil { + f.source.popState(int(f.index)) + } + c.endResolving(f) + f.key, f.source = 0, nil +} + +// resolvingIndex returns 1 + the index in resolutions of the outermost +// computation of key in progress, or 0. +func (c *Checker) resolvingIndex(key memoKey) int { + if i, ok := c.resolving.get(key); ok && int(i) >= c.visibleStart { + return int(i) + 1 + } + return 0 +} + +// resolvingTable maps the keys of computations in progress to the index in +// resolutions of the outermost one. It only holds keys on the resolution +// stack, so it stays small, and it is looked up far more often than it +// changes. It uses open addressing with linear probing, with zero marking an +// empty slot, as no memo key is zero. +type resolvingTable struct { + slots []resolvingSlot + count int +} + +type resolvingSlot struct { + key memoKey + index int32 +} + +func (t *resolvingTable) home(key memoKey) int { + return int(uint64(key) * 0x9E3779B97F4A7C15 >> 32 & uint64(len(t.slots)-1)) +} + +func (t *resolvingTable) get(key memoKey) (int32, bool) { + if t.count == 0 { + return 0, false + } + mask := len(t.slots) - 1 + for i := t.home(key); ; i = (i + 1) & mask { + switch t.slots[i].key { + case key: + return t.slots[i].index, true + case 0: + return 0, false + } + } +} + +// slot returns the slot holding key, or else the empty slot where it would +// be inserted, with room for the insertion. +func (t *resolvingTable) slot(key memoKey) *resolvingSlot { + if 2*(t.count+1) > len(t.slots) { + t.grow() + } + mask := len(t.slots) - 1 + for i := t.home(key); ; i = (i + 1) & mask { + if k := t.slots[i].key; k == key || k == 0 { + return &t.slots[i] + } + } +} + +func (t *resolvingTable) set(key memoKey, index int32) { + if 2*(t.count+1) > len(t.slots) { + t.grow() + } + mask := len(t.slots) - 1 + for i := t.home(key); ; i = (i + 1) & mask { + switch t.slots[i].key { + case key: + t.slots[i].index = index + return + case 0: + t.slots[i] = resolvingSlot{key, index} + t.count++ + return + } + } +} + +func (t *resolvingTable) delete(key memoKey) { + if t.count == 0 { + return + } + mask := len(t.slots) - 1 + i := t.home(key) + for t.slots[i].key != key { + if t.slots[i].key == 0 { + return + } + i = (i + 1) & mask + } + // Shift later entries of the probe sequence back into the hole, so that + // lookups need no tombstones. + for j := (i + 1) & mask; t.slots[j].key != 0; j = (j + 1) & mask { + if h := t.home(t.slots[j].key); (j-h)&mask >= (j-i)&mask { + t.slots[i] = t.slots[j] + i = j + } + } + t.slots[i] = resolvingSlot{} + t.count-- +} + +func (t *resolvingTable) grow() { + old := t.slots + t.slots = make([]resolvingSlot, max(2*len(old), 64)) + t.count = 0 + for _, slot := range old { + if slot.key != 0 { + t.set(slot.key, slot.index) + } + } +} + +// beginResolving records that the resolution about to be pushed computes key, +// unless a computation of key is already in progress, in which case the new +// one re-enters it as the policy of its kind says; see memoReentry. It reports +// whether the resolution is recorded. +func (c *Checker) beginResolving(key memoKey) bool { + slot := c.resolving.slot(key) + if slot.key == 0 { + *slot = resolvingSlot{key, int32(len(c.resolutions))} + c.resolving.count++ + return true + } + i := slot.index + if int(i) >= c.visibleStart { + if memoReentry[key.field()] == reentryCircular { + c.markCircular(int(i)) + } + if memoReentry[key.field()] != reentryResolution { + c.markReadIncomplete(int(i)) + } + return false + } + y := &c.cycleYields[len(c.cycleYields)-1] + y.shadowed = append(y.shadowed, shadowedResolving{key, i}) + slot.index = int32(len(c.resolutions)) + return true +} + +// resolvingAnyIndex returns the index in resolutions of the computation of +// key in progress, if any, whether or not it is visible. A computation hidden +// by a cycle yield is shadowed in Checker.resolving by one re-entering it, +// until the yield ends, even once that one has ended. +func (c *Checker) resolvingAnyIndex(key memoKey) (int, bool) { + if i, ok := c.resolving.get(key); ok { + return int(i), true + } + for _, y := range slices.Backward(c.cycleYields) { + for _, sh := range y.shadowed { + if sh.key == key { + return int(sh.index), true + } + } + } + return 0, false +} + +// endResolving records that the resolution no longer computes its key. A +// resolution that shadows another during a cycle yield ends before the yield +// restores the one it shadows, so it is still the one recorded. +func (c *Checker) endResolving(f *resolution) { + if f.resolving { + c.resolving.delete(f.key) + f.resolving = false + } +} diff --git a/tsc/internal/checker/services.go b/tsc/internal/checker/services.go index 24cd180cb3e84..85e7155c730cc 100644 --- a/tsc/internal/checker/services.go +++ b/tsc/internal/checker/services.go @@ -153,7 +153,7 @@ func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb if reducedType.flags&TypeFlagsStructuredType == 0 { return } - for name, symbol := range c.resolveStructuredTypeMembers(reducedType).getMembers().All() { + for name, symbol := range c.resolveStructuredTypeMembers(reducedType).getMembers(c).All() { if c.isNamedMember(symbol, name) { cb(symbol, name) } @@ -369,22 +369,19 @@ func (c *Checker) runWithoutResolvedSignatureCaching[T any](node *ast.Node, fn f cachedTypes := make(map[*ValueSymbolLinks]*Type) for ancestorNode != nil { signatureLinks := c.signatureLinks.Get(ancestorNode) - cachedResolvedSignatures[signatureLinks] = signatureLinks.resolvedSignature - signatureLinks.resolvedSignature = nil + cachedResolvedSignatures[signatureLinks] = signatureLinks.resolvedSignature.swap(nil) if ast.IsFunctionExpressionOrArrowFunction(ancestorNode) { symbolLinks := c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(ancestorNode)) - resolvedType := symbolLinks.resolvedType - cachedTypes[symbolLinks] = resolvedType - symbolLinks.resolvedType = nil + cachedTypes[symbolLinks] = symbolLinks.resolvedType.swap(nil) } ancestorNode = ast.FindAncestor(ancestorNode.Parent, ast.IsCallLikeOrFunctionLikeExpression) } result := fn() for signatureLinks, resolvedSignature := range cachedResolvedSignatures { - signatureLinks.resolvedSignature = resolvedSignature + signatureLinks.resolvedSignature.swap(resolvedSignature) } for symbolLinks, resolvedType := range cachedTypes { - symbolLinks.resolvedType = resolvedType + symbolLinks.resolvedType.swap(resolvedType) } return result } @@ -870,10 +867,10 @@ func (c *Checker) GetConstantValue(node *ast.Node) any { return c.getEnumMemberValue(node).Value } - if c.symbolNodeLinks.Get(node).resolvedSymbol == nil { + if c.symbolNodeLinks.Get(node).resolvedSymbol.get(c) == nil { c.checkExpressionCached(node) // ensure cached resolved symbol is set } - symbol := c.symbolNodeLinks.Get(node).resolvedSymbol + symbol := c.symbolNodeLinks.Get(node).resolvedSymbol.get(c) if symbol == nil && ast.IsEntityNameExpression(node) { symbol = c.resolveEntityName( node, diff --git a/tsc/internal/checker/symbolaccessibility.go b/tsc/internal/checker/symbolaccessibility.go index 204062db49de0..ad0469f7981ee 100644 --- a/tsc/internal/checker/symbolaccessibility.go +++ b/tsc/internal/checker/symbolaccessibility.go @@ -171,18 +171,37 @@ func (c *Checker) getAlternativeContainingModules(symbol *ast.Symbol, enclosingD return nil } containingFile := ast.GetSourceFileOfNode(enclosingDeclaration) - id := ast.GetNodeId(containingFile.AsNode()) - links := c.symbolContainerLinks.Get(symbol) - if links.extendedContainersByFile == nil { - links.extendedContainersByFile = make(map[ast.NodeId][]*ast.Symbol) + // Try to make an import using an import already in the enclosing file, if possible + if results := c.getImportedContainingModules(symbol, containingFile, enclosingDeclaration); len(results) > 0 { + return results } - existing, ok := links.extendedContainersByFile[id] - if ok && existing != nil { - return existing + // No results from files already being imported by this file - expand search (not location-specific, so cached) + if results, ok := c.extendedContainers.get(c, symbol); ok { + return results + } + return c.extendedContainers.compute(c, symbol, func() []*ast.Symbol { + return c.getExternalModuleContainers(symbol) + }) +} + +type symbolFileKey struct { + symbol *ast.Symbol + file *ast.SourceFile +} + +// getImportedContainingModules returns the modules imported by a file that +// export a symbol. The result only depends on the file, as the module names +// are resolved relative to it. +func (c *Checker) getImportedContainingModules(symbol *ast.Symbol, containingFile *ast.SourceFile, enclosingDeclaration *ast.Node) []*ast.Symbol { + if len(containingFile.Imports()) == 0 { + return nil } - var results []*ast.Symbol - if len(containingFile.Imports()) > 0 { - // Try to make an import using an import already in the enclosing file, if possible + key := symbolFileKey{symbol, containingFile} + if results, ok := c.importedContainingModules.get(c, key); ok { + return results + } + return c.importedContainingModules.compute(c, key, func() []*ast.Symbol { + var results []*ast.Symbol for _, importRef := range containingFile.Imports() { if ast.NodeIsSynthesized(importRef) { // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error @@ -198,23 +217,11 @@ func (c *Checker) getAlternativeContainingModules(symbol *ast.Symbol, enclosingD } results = append(results, resolvedModule) } - if len(results) > 0 { - links.extendedContainersByFile[id] = results - return results - } - } - - if links.extendedContainers != nil { - return *links.extendedContainers - } - // No results from files already being imported by this file - expand search (not location-specific, so cached) - results = c.getExternalModuleContainers(symbol) - links.extendedContainers = &results - return results + return results + }) } type externalModuleContainerIndex struct { - complete bool containersByTarget map[*ast.Symbol][]*ast.Symbol moduleOrder map[*ast.Symbol]int } @@ -227,14 +234,14 @@ func (index *externalModuleContainerIndex) add(target *ast.Symbol, container *as } func (c *Checker) getExternalModuleContainers(symbol *ast.Symbol) []*ast.Symbol { - if c.externalModuleContainers == nil { - c.buildExternalModuleContainerIndex() - } - index := c.externalModuleContainers - if !index.complete { + if _, building := c.resolvingAnyIndex(c.externalModuleContainers.key()); building { // Re-entered from an alias resolved while building the index; answer this query without it. return c.scanExternalModuleContainers(symbol) } + index := c.externalModuleContainers.get(c) + if index == nil { + index = c.buildExternalModuleContainerIndex() + } containers := index.containersByTarget[c.getResolvedTarget(symbol)] parent := c.getParentOfSymbol(symbol) parentOrder, parentIsModule := index.moduleOrder[parent] @@ -250,12 +257,12 @@ func (c *Checker) getExternalModuleContainers(symbol *ast.Symbol) []*ast.Symbol return containers } -func (c *Checker) buildExternalModuleContainerIndex() { +func (c *Checker) buildExternalModuleContainerIndex() *externalModuleContainerIndex { + c.externalModuleContainers.begin(c) index := &externalModuleContainerIndex{ containersByTarget: make(map[*ast.Symbol][]*ast.Symbol), moduleOrder: make(map[*ast.Symbol]int, len(c.program.SourceFiles())), } - c.externalModuleContainers = index for _, file := range c.program.SourceFiles() { if !ast.IsExternalModule(file) { continue @@ -269,7 +276,9 @@ func (c *Checker) buildExternalModuleContainerIndex() { index.add(c.getResolvedTarget(exportEquals), container) } } - index.complete = true + c.externalModuleContainers.set(c, index) + c.endMemo() + return index } func (c *Checker) scanExternalModuleContainers(symbol *ast.Symbol) []*ast.Symbol { @@ -374,7 +383,7 @@ func (c *Checker) getContainersOfSymbol(symbol *ast.Symbol, enclosingDeclaration continue } c.checkExpressionCached(d.Parent.AsBinaryExpression().Left.Expression()) - sym := c.symbolNodeLinks.Get(d.Parent.AsBinaryExpression().Left.Expression()).resolvedSymbol + sym := c.symbolNodeLinks.Get(d.Parent.AsBinaryExpression().Left.Expression()).resolvedSymbol.get(c) if sym != nil && !slices.Contains(candidates, sym) { candidates = append(candidates, sym) } @@ -513,15 +522,11 @@ func (c *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) [ firstRelevantLocation = node return true }) - links := c.symbolContainerLinks.Get(ctx.symbol) - linkKey := accessibleChainCacheKey{ctx.useOnlyExternalAliasing, firstRelevantLocation, ctx.meaning} - if links.accessibleChainCache == nil { - links.accessibleChainCache = make(map[accessibleChainCacheKey][]*ast.Symbol) - } - existing, ok := links.accessibleChainCache[linkKey] - if ok { + key := accessibleChainCacheKey{ctx.symbol, ctx.useOnlyExternalAliasing, firstRelevantLocation, ctx.meaning} + if existing, ok := c.accessibleChains.get(c, key); ok { return existing } + c.accessibleChains.begin(c, key) var result []*ast.Symbol @@ -533,7 +538,8 @@ func (c *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) [ } return false }) - links.accessibleChainCache[linkKey] = result + c.accessibleChains.set(c, key, result) + c.endMemo() return result } @@ -573,10 +579,8 @@ func (c *Checker) getSymbolTableAliases(symbols ast.SymbolTable, tableId symbolT // Cache globals and exports tables (which are large and revisited often). // Locals tables are small and per-scope, so they are filtered but not cached. if kind == stKindGlobals || kind == stKindExports || kind == stKindResolvedExports { - if c.symbolTableAliasCache != nil { - if aliases, ok := c.symbolTableAliasCache[tableId]; ok { - return aliases - } + if aliases, ok := c.symbolTableAliasCache.get(tableId); ok { + return aliases } } var aliases []*ast.Symbol @@ -586,10 +590,7 @@ func (c *Checker) getSymbolTableAliases(symbols ast.SymbolTable, tableId symbolT } } if kind == stKindGlobals || kind == stKindExports || kind == stKindResolvedExports { - if c.symbolTableAliasCache == nil { - c.symbolTableAliasCache = make(map[symbolTableID][]*ast.Symbol) - } - c.symbolTableAliasCache[tableId] = aliases + c.symbolTableAliasCache.intern(tableId, aliases) } return aliases } @@ -882,10 +883,8 @@ func (c *Checker) someSymbolTableInScope( // synthesized table lets someSymbolTableInScope expose them during accessibility checks. func (c *Checker) getClassExpressionNameTable(location *ast.Node) ast.SymbolTable { nodeId := ast.GetNodeId(location) - if c.classExpressionNameTables != nil { - if table, ok := c.classExpressionNameTables[nodeId]; ok { - return table - } + if table, ok := c.classExpressionNameTables.get(nodeId); ok { + return table } classSymbol := c.getSymbolOfDeclaration(location) nameText := location.AsClassExpression().Name().Text() @@ -893,10 +892,7 @@ func (c *Checker) getClassExpressionNameTable(location *ast.Node) ast.SymbolTabl return nil } table := ast.SymbolTableOf(nameText, classSymbol) - if c.classExpressionNameTables == nil { - c.classExpressionNameTables = make(map[ast.NodeId]ast.SymbolTable) - } - c.classExpressionNameTables[nodeId] = table + c.classExpressionNameTables.intern(nodeId, table) return table } diff --git a/tsc/internal/checker/tracer.go b/tsc/internal/checker/tracer.go index 0fea57f8d4d70..1b82937a26c55 100644 --- a/tsc/internal/checker/tracer.go +++ b/tsc/internal/checker/tracer.go @@ -191,7 +191,7 @@ func (a *tracedTypeAdapter) ConditionalTrueType() tracing.TracedType { if a.t.flags&TypeFlagsConditional == 0 { return nil } - t := a.t.AsConditionalType().resolvedTrueType + t := a.t.AsConditionalType().resolvedTrueType.peek() if t == nil { return nil } @@ -202,7 +202,7 @@ func (a *tracedTypeAdapter) ConditionalFalseType() tracing.TracedType { if a.t.flags&TypeFlagsConditional == 0 { return nil } - t := a.t.AsConditionalType().resolvedFalseType + t := a.t.AsConditionalType().resolvedFalseType.peek() if t == nil { return nil } @@ -246,7 +246,7 @@ func (a *tracedTypeAdapter) ReferenceTypeArguments() []tracing.TracedType { if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsReference == 0 { return nil } - return wrapTypes(a.t.AsTypeReference().resolvedTypeArguments) + return wrapTypes(a.t.AsTypeReference().resolvedTypeArguments.peek()) } func (a *tracedTypeAdapter) ReferenceNode() *ast.Node { @@ -304,7 +304,7 @@ func (a *tracedTypeAdapter) EvolvingArrayFinalType() tracing.TracedType { if a.t.flags&TypeFlagsObject == 0 || a.t.objectFlags&ObjectFlagsEvolvingArray == 0 { return nil } - t := a.t.AsEvolvingArrayType().finalArrayType + t := a.t.AsEvolvingArrayType().finalArrayType.peek() if t == nil { return nil } @@ -319,11 +319,12 @@ func (a *tracedTypeAdapter) Pattern() *ast.Node { if a.checker == nil { return nil } - return a.checker.patternForType[a.t] + p, _ := a.checker.patternForType.get(a.t) + return p } func (a *tracedTypeAdapter) RecursionIdentity() any { - return getRecursionIdentity(a.t).value + return a.checker.getRecursionIdentity(a.t).value } func (a *tracedTypeAdapter) Display() string { diff --git a/tsc/internal/checker/types.go b/tsc/internal/checker/types.go index c8f130693f83d..9fcf860e1ff53 100644 --- a/tsc/internal/checker/types.go +++ b/tsc/internal/checker/types.go @@ -181,23 +181,23 @@ type SymbolReferenceLinks struct { // Links for value symbols type ValueSymbolLinks struct { - resolvedType *Type // Type of value symbol + resolvedType memo[*Type] // Type of value symbol target *ast.Symbol mapper *TypeMapper extra *valueSymbolExtra } type valueSymbolExtra struct { - writeType *Type + writeType memo[*Type] nameType *Type containingType *Type // Mapped type for mapped type property, containing union or intersection type for synthetic property } -func (links *ValueSymbolLinks) getWriteType() *Type { +func (links *ValueSymbolLinks) getWriteType(c *Checker) *Type { if links.extra == nil { return nil } - return links.extra.writeType + return links.extra.writeType.get(c) } func (links *ValueSymbolLinks) getNameType() *Type { @@ -221,7 +221,19 @@ func (links *ValueSymbolLinks) setWriteType(c *Checker, typ *Type) { } links.extra = c.valueSymbolExtraArena.New() } - links.extra.writeType = typ + links.extra.writeType.set(c, typ) +} + +// setNewWriteType sets the write type of a symbol being created; see +// memo.setIdentity. +func (links *ValueSymbolLinks) setNewWriteType(c *Checker, typ *Type) { + if links.extra == nil { + if typ == nil { + return + } + links.extra = c.valueSymbolExtraArena.New() + } + links.extra.writeType.setIdentity(c, typ) } func (links *ValueSymbolLinks) setNameType(c *Checker, typ *Type) { @@ -262,10 +274,10 @@ type DeferredSymbolLinks struct { // Links for alias symbols type AliasSymbolLinks struct { - immediateTarget *ast.Symbol // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. - aliasTarget *ast.Symbol // Resolved (non-alias) target of an alias - referenced bool // True if alias symbol has been referenced as a value that can be emitted - typeOnlyDeclaration *ast.Node // First resolved alias declaration that makes the symbol only usable in type constructs + immediateTarget memo[*ast.Symbol] // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. + aliasTarget memo[*ast.Symbol] // Resolved (non-alias) target of an alias + referenced bool // True if alias symbol has been referenced as a value that can be emitted + typeOnlyDeclaration *ast.Node // First resolved alias declaration that makes the symbol only usable in type constructs } // Links for module symbols @@ -273,7 +285,6 @@ type AliasSymbolLinks struct { type ModuleSymbolLinks struct { resolvedExports ast.SymbolTable // Resolved exports of module or combined early- and late-bound static members of a class. typeOnlyExportStarMap map[string]*ast.Node // Set on a module symbol when some of its exports were resolved through a 'export type * from "mod"' declaration - exportsChecked bool } type ReverseMappedSymbolLinks struct { @@ -298,20 +309,15 @@ type ExportTypeLinks struct { // Links for type aliases type TypeAliasLinks struct { - declaredType *Type - typeParameters []*Type // Type parameters of type alias (undefined if non-generic) - instantiations map[CacheHashKey]*Type // Instantiations of generic type alias (undefined if non-generic) - isConstructorDeclaredProperty bool + declaredType memoReported[*Type] + typeParameters []*Type // Type parameters of type alias (undefined if non-generic) + instantiations memoMap[CacheHashKey, *Type] // Instantiations of generic type alias (undefined if non-generic) } // Links for declared types (type parameters, class types, interface types, enums) type DeclaredTypeLinks struct { - declaredType *Type - interfaceChecked bool - indexSignaturesChecked bool - typeParametersChecked bool - enumChecked bool + declaredType memo[*Type] } // Links for switch clauses @@ -326,11 +332,10 @@ const ( ) type SwitchStatementLinks struct { - exhaustiveState ExhaustiveState // Switch statement exhaustiveness - switchTypesComputed bool - witnessesComputed bool - switchTypes []*Type - witnesses []string + exhaustiveState memo[ExhaustiveState] // Switch statement exhaustiveness + witnessesComputed bool + switchTypes memo[[]*Type] + witnesses []string // Syntactic, so needs no memo } type ArrayLiteralLinks struct { @@ -360,7 +365,7 @@ type SpreadLinks struct { // Links for variances of type aliases and interface types type VarianceLinks struct { - variances []VarianceFlags + variances memo[[]VarianceFlags] } type VarianceFlags uint32 @@ -383,17 +388,12 @@ type MarkedAssignmentSymbolLinks struct { } type accessibleChainCacheKey struct { + symbol *ast.Symbol useOnlyExternalAliasing bool location *ast.Node meaning ast.SymbolFlags } -type ContainingSymbolLinks struct { - extendedContainersByFile map[ast.NodeId][]*ast.Symbol // Symbols of nodes which which logically contain this one, cached by file the request is made within - extendedContainers *[]*ast.Symbol // Containers (other than the parent) which this symbol is aliased in - accessibleChainCache map[accessibleChainCacheKey][]*ast.Symbol -} - type AccessFlags uint32 const ( @@ -414,14 +414,11 @@ type NodeCheckFlags uint32 const ( NodeCheckFlagsNone NodeCheckFlags = 0 - NodeCheckFlagsTypeChecked NodeCheckFlags = 1 << 0 // Node has been type checked NodeCheckFlagsContextChecked NodeCheckFlags = 1 << 6 // Contextual types have been assigned NodeCheckFlagsSignatureContextChecked NodeCheckFlags = 1 << 7 // Signature of a contextually typed function has been checked NodeCheckFlagsEnumValuesComputed NodeCheckFlags = 1 << 10 // Values for enum members have been computed, and any errors have been reported for them. - NodeCheckFlagsAssignmentsMarked NodeCheckFlags = 1 << 17 // Parameter assignments have been marked NodeCheckFlagsContainsClassWithPrivateIdentifiers NodeCheckFlags = 1 << 20 // Marked on all block-scoped containers containing a class with private identifiers. NodeCheckFlagsContainsSuperPropertyInStaticInitializer NodeCheckFlags = 1 << 21 // Marked on all block-scoped containers containing a static initializer with 'super.x' or 'super[x]'. - NodeCheckFlagsInCheckIdentifier NodeCheckFlags = 1 << 22 NodeCheckFlagsInitializerIsUndefined NodeCheckFlags = 1 << 24 NodeCheckFlagsInitializerIsUndefinedComputed NodeCheckFlags = 1 << 25 ) @@ -431,39 +428,42 @@ const ( type NodeLinks struct { flags NodeCheckFlags // Set of flags specific to Node declarationRequiresScopeChange core.Tristate // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. - hasReportedStatementInAmbientContext bool // Cache boolean if we report statements in ambient context + hasReportedStatementInAmbientContext memo[bool] // Cache boolean if we report statements in ambient context } type SymbolNodeLinks struct { - resolvedSymbol *ast.Symbol // Resolved symbol associated with node + resolvedSymbol memo[*ast.Symbol] // Resolved symbol associated with node } type TypeNodeLinks struct { - resolvedType *Type // Resolved type associated with node - outerTypeParameters *[]*Type // Outer type parameters of anonymous object type + resolvedType memo[*Type] // Resolved type associated with node + outerTypeParameters memo[*[]*Type] // Outer type parameters of anonymous object type } -func (links *TypeNodeLinks) getOuterTypeParameters() []*Type { - if links.outerTypeParameters == nil { - return nil +func (links *TypeNodeLinks) getOuterTypeParameters(c *Checker) []*Type { + if p := links.outerTypeParameters.get(c); p != nil { + return *p } - return *links.outerTypeParameters + return nil } func (links *TypeNodeLinks) setOuterTypeParameters(c *Checker, types []*Type) { if types == nil { - links.outerTypeParameters = nil + links.outerTypeParameters.set(c, nil) return } - if links.outerTypeParameters == nil { - links.outerTypeParameters = c.outerTypeParameterListArena.New() - } - *links.outerTypeParameters = types + p := c.outerTypeParameterListArena.New() + *p = types + links.outerTypeParameters.set(c, p) } type ComputedNameNodeLinks struct { - hasName core.Tristate // If the node has a computable name - name string // Resolved name associated with the type of the node + name memo[computedName] // Resolved name associated with the type of the node +} + +type computedName struct { + name string + exists core.Tristate // If the node has a computable name; unknown until computed } // Links for enum members @@ -478,6 +478,7 @@ type SourceFileLinks struct { typeChecked bool unusedChecked bool externalHelpersModule *ast.Symbol + externalHelperRequests []externalHelperRequest requestedExternalEmitHelpers ExternalEmitHelpers deferredNodes collections.OrderedSet[*ast.Node] identifierCheckNodes []*ast.Node @@ -485,15 +486,15 @@ type SourceFileLinks struct { localJsxFragmentNamespace string localJsxFactory *ast.EntityName localJsxFragmentFactory *ast.EntityName - jsxFragmentType *Type + jsxFragmentType memo[*Type] } // Signature specific links type SignatureLinks struct { - resolvedSignature *Signature // Cached signature of signature node or call expression - effectsSignature *Signature // Signature with possible control flow effects - decoratorSignature *Signature // Signature for decorator as if invoked by the runtime + resolvedSignature memo[*Signature] // Cached signature of signature node or call expression + effectsSignature memo[*Signature] // Signature with possible control flow effects + decoratorSignature memo[*Signature] // Signature for decorator as if invoked by the runtime } type TypeFlags uint32 @@ -707,9 +708,8 @@ const ( ObjectFlagsSingleSignatureType = 1 << 25 // A single signature type extracted from a potentially broader type ObjectFlagsIsClassInstanceClone = 1 << 26 // Type is a clone of a class instance type // Flags that require TypeFlags.Object and ObjectFlags.Reference - ObjectFlagsIdenticalBaseTypeCalculated = 1 << 27 // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already - ObjectFlagsIdenticalBaseTypeExists = 1 << 28 // has a defined cachedEquivalentBaseType member - ObjectFlagsFromTypeNode = 1 << 29 // Originates in resolution of AST type node + ObjectFlagsIdenticalBaseTypeExists = 1 << 28 // has a defined cachedEquivalentBaseType member + ObjectFlagsFromTypeNode = 1 << 29 // Originates in resolution of AST type node // Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution ObjectFlagsIsGenericTypeComputed = 1 << 22 // IsGenericObjectType flag has been computed ObjectFlagsIsGenericObjectType = 1 << 23 // Union or intersection contains generic object type @@ -900,6 +900,14 @@ func (t *Type) AsStructuredType() *StructuredType { return nil } +// asResolvedStructuredType is AsStructuredType for a type known to be +// structured, such as one with resolved members, without checking. +func (t *Type) asResolvedStructuredType() *StructuredType { + // SAFETY: Callers only pass types of the kinds AsStructuredType accepts, + // which embed StructuredType at offset zero. + return (*StructuredType)(unsafe.Pointer(t)) +} + func (t *Type) AsObjectType() *ObjectType { if t.kind >= typeKindObject && t.kind <= typeKindEvolvingArray { // SAFETY: Types of these kinds are structs that embed ObjectType at offset zero. @@ -1109,7 +1117,7 @@ type UniqueESSymbolType struct { type ConstrainedType struct { TypeBase - resolvedBaseConstraint *Type + resolvedBaseConstraint memo[*Type] } func (t *ConstrainedType) AsConstrainedType() *ConstrainedType { return t } @@ -1147,7 +1155,7 @@ func (t *StructuredType) ConstructSignatures() []*Signature { return slices.Clip(t.extra.signatures[t.extra.callSignatureCount:]) } -func (t *StructuredType) getMember(name string) *ast.Symbol { +func (t *StructuredType) getMember(c *Checker, name string) *ast.Symbol { if t.members != nil { return t.members.Get(name) } @@ -1159,12 +1167,12 @@ func (t *StructuredType) getMember(name string) *ast.Symbol { if index < 0 { return nil } - return t.getPropertyAt(index) + return t.getPropertyAt(c, index) } -func (t *StructuredType) getMembers() ast.SymbolTable { +func (t *StructuredType) getMembers(c *Checker) ast.SymbolTable { if t.members == nil { - if properties := t.getProperties(); len(properties) != 0 { + if properties := t.getProperties(c); len(properties) != 0 { t.members = ast.NewSymbolTableWithCapacity(len(properties)) for _, member := range properties { t.members.Set(member.Name(), member) @@ -1174,7 +1182,7 @@ func (t *StructuredType) getMembers() ast.SymbolTable { return t.members } -func (t *StructuredType) getPropertyAt(index int) *ast.Symbol { +func (t *StructuredType) getPropertyAt(c *Checker, index int) *ast.Symbol { lazy := t.getLazyState() if lazy == nil { return t.properties[index] @@ -1187,19 +1195,22 @@ func (t *StructuredType) getPropertyAt(index int) *ast.Symbol { symbol := lazy.layout.members.SymbolAt(index) baseIndex := lazy.layout.baseIndices[index] if baseIndex < 0 { - member = t.checker.instantiateSymbol(symbol, lazy.mapper) + member = c.instantiateSymbol(symbol, lazy.mapper) } else { - member = t.checker.getPropertyOfType(lazy.baseTypes[baseIndex], symbol.Name()) + member = c.getPropertyOfType(lazy.baseTypes[baseIndex], symbol.Name()) } t.properties[index] = member + if c.logResults() { + c.logPropertySlotDiscard(t, lazy, index) + } } return member } -func (t *StructuredType) getProperties() []*ast.Symbol { +func (t *StructuredType) getProperties(c *Checker) []*ast.Symbol { if lazy := t.getLazyState(); lazy != nil && !lazy.propertiesResolved { for i := range lazy.layout.members.Len() { - t.getPropertyAt(i) + t.getPropertyAt(c, i) } lazy.propertiesResolved = true } @@ -1246,7 +1257,7 @@ func (t *StructuredType) ensureExtra() *structuredTypeExtra { } func (t *StructuredType) Properties() []*ast.Symbol { - return t.getProperties() + return t.getProperties(t.checker) } // Except for tuple type references and reverse mapped types, all object types have an associated symbol. @@ -1284,9 +1295,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[CacheHashKey]*Type // Map of type instantiations + target *Type // Target of instantiated type + mapper *TypeMapper // Type mapper for instantiated type + instantiations memoMap[CacheHashKey, *Type] // Map of type instantiations } func (t *ObjectType) AsObjectType() *ObjectType { return t } @@ -1296,7 +1307,7 @@ func (t *ObjectType) AsObjectType() *ObjectType { return t } type TypeReference struct { ObjectType node *ast.Node // TypeReferenceNode | ArrayTypeNode | TupleTypeNode when deferred, else nil - resolvedTypeArguments []*Type + resolvedTypeArguments memo[[]*Type] lazy *LazyState // Resolved member layout and instantiation state } @@ -1325,8 +1336,8 @@ type InterfaceType struct { thisType *Type // The "this" type (nil if none) baseTypesResolved bool declaredMembersResolved bool - resolvedBaseConstructorType *Type - resolvedBaseTypes []*Type + resolvedBaseConstructorType memo[*Type] + resolvedBaseTypes memo[[]*Type] declaredMembers ast.SymbolTable // Declared members declaredCallSignatures []*Signature // Declared call signatures declaredConstructSignatures []*Signature // Declared construct signatures @@ -1416,19 +1427,19 @@ type InstantiationExpressionType struct { type MappedType struct { ObjectType declaration *ast.MappedTypeNode - typeParameter *Type - constraintType *Type - nameType *Type - templateType *Type - modifiersType *Type - resolvedApparentType *Type - containsError bool -} - -func (t *MappedType) TypeParameter() *Type { return t.typeParameter } -func (t *MappedType) ConstraintType() *Type { return t.constraintType } -func (t *MappedType) NameType() *Type { return t.nameType } -func (t *MappedType) TemplateType() *Type { return t.templateType } + typeParameter memo[*Type] + constraintType memo[*Type] + nameType memo[*Type] + templateType memo[*Type] + modifiersType memo[*Type] + resolvedApparentType memo[*Type] + containsError memo[bool] +} + +func (t *MappedType) TypeParameter() *Type { return t.typeParameter.peek() } +func (t *MappedType) ConstraintType() *Type { return t.constraintType.peek() } +func (t *MappedType) NameType() *Type { return t.nameType.peek() } +func (t *MappedType) TemplateType() *Type { return t.templateType.peek() } func (t *MappedType) ResolveComponents(c *Checker, typ *Type) { c.getTypeParameterFromMappedType(typ) c.getConstraintTypeFromMappedType(typ) @@ -1450,7 +1461,7 @@ type ReverseMappedType struct { type EvolvingArrayType struct { ObjectType elementType *Type - finalArrayType *Type + finalArrayType memo[*Type] } // UnionOrIntersectionTypeData @@ -1458,9 +1469,9 @@ type EvolvingArrayType struct { type UnionOrIntersectionType struct { StructuredType types []*Type - propertyCache ast.SymbolTable // Augmented entries, except complete ones already in the non-augmented cache. - propertyCacheWithoutFunctionPropertyAugment ast.SymbolTable - resolvedProperties []*ast.Symbol + propertyCache memoMap[string, *ast.Symbol] // Augmented entries, except complete ones already in the non-augmented cache. + propertyCacheWithoutFunctionPropertyAugment memoMap[string, *ast.Symbol] + resolvedProperties memo[[]*ast.Symbol] } func (t *UnionOrIntersectionType) AsUnionOrIntersectionType() *UnionOrIntersectionType { return t } @@ -1473,10 +1484,10 @@ func (t *UnionOrIntersectionType) Types() []*Type { type UnionType struct { UnionOrIntersectionType - resolvedReducedType *Type + resolvedReducedType memo[*Type] regularType *Type origin *Type // Denormalized union, intersection, or index type in which union originates - keyPropertyName string // Property with unique unit type that exists in every object/intersection in union type + keyPropertyName memo[string] // Property with unique unit type that exists in every object/intersection in union type constituentMap map[*Type]*Type // Constituents keyed by unit type discriminants } @@ -1484,21 +1495,21 @@ type UnionType struct { type IntersectionType struct { UnionOrIntersectionType - resolvedApparentType *Type - uniqueLiteralFilledInstantiation *Type // Instantiation with type parameters mapped to never type + resolvedApparentType memo[*Type] + uniqueLiteralFilledInstantiation memo[*Type] // Instantiation with type parameters mapped to never type } // TypeParameter type TypeParameter struct { ConstrainedType - constraint *Type + constraint memo[*Type] target *Type mapper *TypeMapper isThisType bool isDistributed bool - resolvedDefaultType *Type - distributedType *Type + resolvedDefaultType memo[*Type] + distributedType *Type // The distributed counterpart, or for a distributed type parameter, its original origin *Type // The type parameter a restrictive type parameter was made from, or the tuple target of a tuple element type parameter } @@ -1569,7 +1580,7 @@ type ConditionalRoot struct { isDistributive bool inferTypeParameters []*Type outerTypeParameters []*Type - instantiations map[CacheHashKey]*Type + instantiations memoMap[CacheHashKey, *Type] alias *TypeAlias } @@ -1578,11 +1589,11 @@ type ConditionalType struct { root *ConditionalRoot checkType *Type extendsType *Type - resolvedTrueType *Type - resolvedFalseType *Type - resolvedInferredTrueType *Type // The `trueType` instantiated with the `combinedMapper`, if present - resolvedDefaultConstraint *Type - resolvedConstraintOfDistributive *Type + resolvedTrueType memo[*Type] + resolvedFalseType memo[*Type] + resolvedInferredTrueType memo[*Type] // The `trueType` instantiated with the `combinedMapper`, if present + resolvedDefaultConstraint memo[*Type] + resolvedConstraintOfDistributive memo[*Type] mapper *TypeMapper combinedMapper *TypeMapper } @@ -1621,10 +1632,10 @@ type Signature struct { id SignatureId flags SignatureFlags minArgumentCount int32 - resolvedMinArgumentCount int32 + resolvedMinArgumentCount memo[int32] declaration *ast.Node parameters []*ast.Symbol - resolvedReturnType *Type + resolvedReturnType memo[*Type] target *Signature mapper *TypeMapper extra *signatureExtra @@ -1667,7 +1678,20 @@ func (s *Signature) getResolvedTypePredicate(c *Checker) *TypePredicate { return s.extra.resolvedTypePredicate } +// setResolvedTypePredicate caches the type predicate of the signature, a flag +// memo; see initResolvedTypePredicate. func (s *Signature) setResolvedTypePredicate(c *Checker, predicate *TypePredicate) { + if c.logResults() { + old := s.getResolvedTypePredicate(c) + c.logMemoDiscard(nil, memoKeyOf(s, memoTypePredicate), func() { s.initResolvedTypePredicate(c, old) }) + } + s.initResolvedTypePredicate(c, predicate) + c.setFlagMemo(memoKeyOf(s, memoTypePredicate)) +} + +// initResolvedTypePredicate sets the type predicate of a signature being +// created. +func (s *Signature) initResolvedTypePredicate(c *Checker, predicate *TypePredicate) { s.flags &^= signatureFlagsNoTypePredicate if predicate != nil && predicate == c.noTypePredicate { s.flags |= signatureFlagsNoTypePredicate diff --git a/tsc/internal/checker/types_test.go b/tsc/internal/checker/types_test.go index 6e0f19446fe00..63ec6affc72df 100644 --- a/tsc/internal/checker/types_test.go +++ b/tsc/internal/checker/types_test.go @@ -2,6 +2,7 @@ package checker import ( "encoding/binary" + "math/rand/v2" "runtime" "slices" "strconv" @@ -18,8 +19,8 @@ import ( func TestCheckerNodeLinkStores(t *testing.T) { t.Parallel() f := ast.NewNodeFactory(ast.NodeFactoryHooks{}) - first := &Checker{} - second := &Checker{} + first := newBareTestChecker() + second := newBareTestChecker() nodes := make([]*ast.Node, 4096) for i := range nodes { node := f.NewIdentifier(strconv.Itoa(i)) @@ -29,18 +30,18 @@ func TestCheckerNodeLinkStores(t *testing.T) { nodes[i] = node assert.Assert(t, !first.typeNodeLinks.Has(node)) assert.Assert(t, !first.signatureLinks.Has(node)) - first.typeNodeLinks.Get(node).resolvedType = &Type{id: TypeId(i + 1)} - first.signatureLinks.Get(node).resolvedSignature = &Signature{id: SignatureId(i + 1)} - second.typeNodeLinks.Get(node).resolvedType = &Type{id: TypeId(i + 2)} - second.signatureLinks.Get(node).resolvedSignature = &Signature{id: SignatureId(i + 2)} + first.typeNodeLinks.Get(node).resolvedType.intern(&Type{id: TypeId(i + 1)}) + first.signatureLinks.Get(node).resolvedSignature.intern(&Signature{id: SignatureId(i + 1)}) + second.typeNodeLinks.Get(node).resolvedType.intern(&Type{id: TypeId(i + 2)}) + second.signatureLinks.Get(node).resolvedSignature.intern(&Signature{id: SignatureId(i + 2)}) } f.ReleaseArenas() runtime.GC() for i, node := range nodes { - assert.Equal(t, first.typeNodeLinks.Get(node).resolvedType.id, TypeId(i+1)) - assert.Equal(t, first.signatureLinks.Get(node).resolvedSignature.id, SignatureId(i+1)) - assert.Equal(t, second.typeNodeLinks.Get(node).resolvedType.id, TypeId(i+2)) - assert.Equal(t, second.signatureLinks.Get(node).resolvedSignature.id, SignatureId(i+2)) + assert.Equal(t, first.typeNodeLinks.Get(node).resolvedType.get(first).id, TypeId(i+1)) + assert.Equal(t, first.signatureLinks.Get(node).resolvedSignature.get(first).id, SignatureId(i+1)) + assert.Equal(t, second.typeNodeLinks.Get(node).resolvedType.get(second).id, TypeId(i+2)) + assert.Equal(t, second.signatureLinks.Get(node).resolvedSignature.get(second).id, SignatureId(i+2)) } clone := nodes[0].Clone(f) assert.Assert(t, !first.typeNodeLinks.Has(clone)) @@ -85,7 +86,7 @@ func TestTypeCasts(t *testing.T) { {typeKindIntersection, &IntersectionType{}, func(t *Type) any { return t.AsIntersectionType() }}, } for _, test := range tests { - c := &Checker{} + c := newBareTestChecker() typ := c.newType(TypeFlagsAny, ObjectFlagsNone, test.data.AsType(), test.kind) runtime.GC() assert.Equal(t, test.cast(typ), any(test.data)) @@ -175,15 +176,17 @@ func BenchmarkTypeListKey(b *testing.B) { } } +// newBareTestChecker returns an uninitialized checker with an empty store. +func newBareTestChecker() *Checker { + c := &Checker{memoStore: &memoStore{}} + c.initResolutions() + c.root = c + return c +} + func newCompositeTestChecker() (*Checker, []*Type) { - c := &Checker{ - strictNullChecks: true, - unionTypes: make(map[CacheHashKey]*Type), - unionOfUnionTypes: make(map[UnionOfUnionKey]*Type), - intersectionTypes: make(map[CacheHashKey]*Type), - intersectionContentTypes: make(map[CacheHashKey]*Type), - subtypeReductionCache: make(map[CacheHashKey][]*Type), - } + c := newBareTestChecker() + c.strictNullChecks = true c.compareSymbols = c.compareSymbolsWorker c.neverType = c.newIntrinsicType(TypeFlagsNever, "never") c.unknownType = c.newIntrinsicType(TypeFlagsUnknown, "unknown") @@ -307,7 +310,7 @@ func TestIntersectionReductionAllocations(t *testing.T) { //nolint:paralleltest func TestTemporaryPropertyCounts(t *testing.T) { t.Parallel() - c := &Checker{} + c := newBareTestChecker() names := func(p *temporaryPropertyCounts) []string { var result []string for _, e := range p.entries { @@ -349,7 +352,7 @@ func TestTemporaryPropertyCounts(t *testing.T) { assert.Equal(t, counts.Size(), 0) } assert.Assert(t, c.getTemporaryPropertyCounts() != reused) - other := &Checker{} + other := newBareTestChecker() assert.Assert(t, other.getTemporaryPropertyCounts() != outer) } @@ -367,7 +370,7 @@ func TestIntersectionReductionReentrancy(t *testing.T) { assert.Assert(t, c.freePropertyCounts.next != nil) workspace := c.freePropertyCounts conflicting := c.newSymbolEx(ast.SymbolFlagsProperty, "value", ast.CheckFlagsContainsPrivate) - outer.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment.Set("value", conflicting) + outer.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment.intern("value", conflicting) assert.Assert(t, c.somePropertyReducesToNever(outer)) assert.Equal(t, c.freePropertyCounts, workspace) for counts := c.freePropertyCounts; counts != nil; counts = counts.next { @@ -489,7 +492,7 @@ func TestCompactValueSymbolLinksLayout(t *testing.T) { assert.Equal(t, unsafe.Sizeof(valueSymbolExtra{}), 3*unsafe.Sizeof(uintptr(0))) c, types := newCompositeTestChecker() var links ValueSymbolLinks - assert.Assert(t, links.getWriteType() == nil) + assert.Assert(t, links.getWriteType(c) == nil) assert.Assert(t, links.getNameType() == nil) assert.Assert(t, links.getContainingType() == nil) links.setWriteType(c, nil) @@ -506,12 +509,12 @@ func TestCompactValueSymbolLinksLayout(t *testing.T) { other.setNameType(c, types[2]) } runtime.GC() - assert.Equal(t, links.getWriteType(), types[0]) + assert.Equal(t, links.getWriteType(c), types[0]) assert.Equal(t, links.getNameType(), types[1]) assert.Equal(t, links.getContainingType(), types[2]) links.setNameType(c, nil) assert.Assert(t, links.getNameType() == nil) - assert.Equal(t, links.getWriteType(), types[0]) + assert.Equal(t, links.getWriteType(c), types[0]) assert.Equal(t, links.getContainingType(), types[2]) } @@ -540,9 +543,9 @@ func TestUnionIntersectionPropertyCacheSharing(t *testing.T) { assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, !augmentedFirst), symbol) assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, augmentedFirst), symbol) } - assert.Equal(t, data.propertyCacheWithoutFunctionPropertyAugment.Len(), members.Len()) + assert.Equal(t, len(data.propertyCacheWithoutFunctionPropertyAugment.memoEntries), members.Len()) if !augmentedFirst { - assert.Assert(t, data.propertyCache == nil) + assert.Assert(t, data.propertyCache.isEmpty()) } runtime.GC() for name, symbol := range members.All() { @@ -570,25 +573,23 @@ func TestUnionIntersectionPropertyCacheModes(t *testing.T) { data := typ.AsUnionOrIntersectionType() assert.Assert(t, c.getUnionOrIntersectionProperty(typ, "missing", true) == nil) assert.Assert(t, c.getUnionOrIntersectionProperty(typ, "missing", false) == nil) - assert.Assert(t, data.propertyCache == nil) - assert.Assert(t, data.propertyCacheWithoutFunctionPropertyAugment == nil) - data.propertyCacheWithoutFunctionPropertyAugment = ast.NewSymbolTable() - data.propertyCache = ast.NewSymbolTable() + assert.Assert(t, data.propertyCache.isEmpty()) + assert.Assert(t, data.propertyCacheWithoutFunctionPropertyAugment.isEmpty()) for _, flags := range []ast.CheckFlags{ast.CheckFlagsReadPartial, ast.CheckFlagsWritePartial} { name := strconv.Itoa(int(flags)) partial := c.newSymbolEx(ast.SymbolFlagsProperty, name, flags) complete := c.newSymbol(ast.SymbolFlagsProperty, name) - data.propertyCacheWithoutFunctionPropertyAugment.Set(name, partial) + data.propertyCacheWithoutFunctionPropertyAugment.intern(name, partial) assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, true), partial) assert.Assert(t, c.getUnionOrIntersectionProperty(typ, name, false) == nil) assert.Equal(t, c.getPropertyOfUnionOrIntersectionType(typ, name, true) == nil, flags&ast.CheckFlagsReadPartial != 0) - data.propertyCache.Set(name, complete) + data.propertyCache.intern(name, complete) assert.Equal(t, c.getUnionOrIntersectionProperty(typ, name, false), complete) } nonAugmented := c.newSymbol(ast.SymbolFlagsProperty, "different") augmented := c.newSymbol(ast.SymbolFlagsProperty, "different") - data.propertyCacheWithoutFunctionPropertyAugment.Set("different", nonAugmented) - data.propertyCache.Set("different", augmented) + data.propertyCacheWithoutFunctionPropertyAugment.intern("different", nonAugmented) + data.propertyCache.intern("different", augmented) assert.Equal(t, c.getUnionOrIntersectionProperty(typ, "different", true), nonAugmented) assert.Equal(t, c.getUnionOrIntersectionProperty(typ, "different", false), augmented) }) @@ -650,30 +651,28 @@ func TestCompactTypeNodeLinksLayout(t *testing.T) { assert.Equal(t, unsafe.Sizeof(TypeNodeLinks{}), 2*unsafe.Sizeof(uintptr(0))) c, types := newCompositeTestChecker() var links TypeNodeLinks - links.resolvedType = types[0] - assert.Assert(t, links.getOuterTypeParameters() == nil) + links.resolvedType.intern(types[0]) + assert.Assert(t, links.getOuterTypeParameters(c) == nil) links.setOuterTypeParameters(c, nil) - assert.Assert(t, links.outerTypeParameters == nil) + assert.Assert(t, links.outerTypeParameters.memoValue == nil) links.setOuterTypeParameters(c, []*Type{}) - assert.Assert(t, links.getOuterTypeParameters() != nil) - assert.Equal(t, len(links.getOuterTypeParameters()), 0) - entry := links.outerTypeParameters + assert.Assert(t, links.getOuterTypeParameters(c) != nil) + assert.Equal(t, len(links.getOuterTypeParameters(c)), 0) list := slices.Clone(types) links.setOuterTypeParameters(c, list) - assert.Equal(t, links.outerTypeParameters, entry) - assert.Equal(t, links.getOuterTypeParameters()[0], types[0]) + assert.Equal(t, links.getOuterTypeParameters(c)[0], types[0]) list[0] = types[1] - assert.Equal(t, links.getOuterTypeParameters()[0], types[1]) + assert.Equal(t, links.getOuterTypeParameters(c)[0], types[1]) for range 1024 { var other TypeNodeLinks other.setOuterTypeParameters(c, types) } runtime.GC() - assert.Equal(t, links.getOuterTypeParameters()[0], types[1]) - assert.Equal(t, links.resolvedType, types[0]) + assert.Equal(t, links.getOuterTypeParameters(c)[0], types[1]) + assert.Equal(t, links.resolvedType.get(c), types[0]) links.setOuterTypeParameters(c, nil) - assert.Assert(t, links.getOuterTypeParameters() == nil) - assert.Equal(t, links.resolvedType, types[0]) + assert.Assert(t, links.getOuterTypeParameters(c) == nil) + assert.Equal(t, links.resolvedType.get(c), types[0]) runtime.KeepAlive(c) } @@ -694,7 +693,7 @@ func TestSignatureExtra(t *testing.T) { assert.Assert(t, sig.getComposite() == nil) assert.Assert(t, sig.getIsolatedSignatureType() == nil) assert.Assert(t, sig.getResolvedTypePredicate(c) == nil) - assert.Equal(t, sig.resolvedMinArgumentCount, int32(-1)) + assert.Equal(t, sig.resolvedMinArgumentCount.peek(), int32(-1)) sig.setTypeParameters(c, nil) sig.setThisParameter(c, nil) sig.setComposite(c, nil) @@ -749,8 +748,36 @@ func TestSignatureExtra(t *testing.T) { sig.setComposite(c, nil) assert.Equal(t, sig.extra, entry) assert.Assert(t, sig.TypeParameters() == nil) - assert.Equal(t, sig.resolvedReturnType, types[0]) + assert.Equal(t, sig.resolvedReturnType.get(c), types[0]) assert.Equal(t, sig.minArgumentCount, int32(1)) assert.Equal(t, sig.getResolvedTypePredicate(c), predicate) runtime.KeepAlive(c) } + +func TestResolvingTable(t *testing.T) { + t.Parallel() + var table resolvingTable + want := map[memoKey]int32{} + rng := rand.New(rand.NewPCG(1, 2)) + for i := range 200000 { + key := memoKey(rng.IntN(300)+1) << 8 + switch rng.IntN(3) { + case 0: + table.set(key, int32(i)) + want[key] = int32(i) + case 1: + table.delete(key) + delete(want, key) + } + got, ok := table.get(key) + wantIndex, wantOk := want[key] + assert.Equal(t, ok, wantOk) + assert.Equal(t, got, wantIndex) + assert.Equal(t, table.count, len(want)) + } + for key, index := range want { + got, ok := table.get(key) + assert.Assert(t, ok) + assert.Equal(t, got, index) + } +} diff --git a/tsc/internal/checker/utilities.go b/tsc/internal/checker/utilities.go index bdc55ba66c932..9aedc51fce74f 100644 --- a/tsc/internal/checker/utilities.go +++ b/tsc/internal/checker/utilities.go @@ -375,6 +375,15 @@ func (c *Checker) compareSymbolsWorker(s1, s2 *ast.Symbol) int { if r := c.compareSymbolDeclarationsAndNames(s1, s2); r != 0 { return r } + // Symbols the checker derives from a declared one, such as the members of + // mapped types, share its declaration and name. Symbol IDs are assigned in + // the order symbols are first used, so order those by how they were made. + if r := cmp.Compare(s1.Flags()&ast.SymbolFlagsTransient, s2.Flags()&ast.SymbolFlagsTransient); r != 0 { + return r + } + if r := cmp.Compare(s1.CheckFlags(), s2.CheckFlags()); r != 0 { + return r + } // Fall back to symbol IDs. This is a last resort that should happen only when symbols have // no declaration and duplicate names. return cmp.Compare(ast.GetSymbolId(s1), ast.GetSymbolId(s2)) @@ -478,7 +487,7 @@ func CompareTypes(t1, t2 *Type) int { // Here we know we have references to instantiations of the same type because we have matching targets. if r1.node == nil && r2.node == nil { // Non-deferred type references with the same target are sorted by their type argument lists. - if c := compareTypeLists(t1.AsTypeReference().resolvedTypeArguments, t2.AsTypeReference().resolvedTypeArguments); c != 0 { + if c := compareTypeLists(t1.AsTypeReference().resolvedTypeArguments.peek(), t2.AsTypeReference().resolvedTypeArguments.peek()); c != 0 { return c } } else { @@ -663,8 +672,8 @@ func CompareTypes(t1, t2 *Type) int { } // Types implied by binding patterns are ordered by their patterns. if t1.flags&TypeFlagsObject != 0 { - p1 := t1.checker.patternForType[t1] - p2 := t2.checker.patternForType[t2] + p1, _ := t1.checker.patternForType.get(t1) + p2, _ := t2.checker.patternForType.get(t2) if c := t1.checker.compareNodes(p1, p2); c != 0 { return c } @@ -822,7 +831,7 @@ func compareObjectMembers(t1, t2 *Type, withTypes bool) int { // created. func (c *Checker) peekTypeOfObjectLiteralMember(s *ast.Symbol) *Type { if links := c.valueSymbolLinks.TryGet(s); links != nil { - return links.resolvedType + return links.resolvedType.peek() } return nil } @@ -869,7 +878,7 @@ func compareTypeParameterOrigins(t1, t2 *Type) int { // Tuple this types have no symbol; their constraint is the tuple target, // initialized at creation. if t1.symbol == nil { - return CompareTypes(p1.constraint, p2.constraint) + return CompareTypes(p1.constraint.peek(), p2.constraint.peek()) } case typeParameterOriginDistributed: return CompareTypes(p1.distributedType, p2.distributedType) diff --git a/tsc/internal/execute/tsctests/tsc_test.go b/tsc/internal/execute/tsctests/tsc_test.go index ee429a826f94a..befb7578e9184 100644 --- a/tsc/internal/execute/tsctests/tsc_test.go +++ b/tsc/internal/execute/tsctests/tsc_test.go @@ -2406,12 +2406,10 @@ func TestTscIncremental(t *testing.T) { edit: func(sys *TestSys) { sys.appendFile("/home/src/workspaces/project/repro.ts", "\n// comment-only edit\n") }, - expectedDiff: "Like Strada, signature generation produces the missing-global diagnostic before semantic checking, so it is excluded from the file's semantic diagnostics.", }, { - caption: "no change", - edit: noChange.edit, - expectedDiff: "Like Strada, the cached semantic diagnostics do not include the missing-global diagnostic produced during signature generation.", + caption: "no change", + edit: noChange.edit, }, { caption: "delete build info to restore the semantic diagnostic", diff --git a/tsc/internal/testrunner/compiler_runner.go b/tsc/internal/testrunner/compiler_runner.go index 3ea3d64238042..3efc2ec3e71ca 100644 --- a/tsc/internal/testrunner/compiler_runner.go +++ b/tsc/internal/testrunner/compiler_runner.go @@ -167,21 +167,8 @@ func (r *CompilerBaselineRunner) runTest(t *testing.T, filename string) { } } -// When a program checks its files on several checkers, these tests can see different results depending on which -// checker answers a query first. -var concurrentSkippedTests = map[string]string{ - "checkOrderCircularDefaults.ts": "Circular default results depend on which checker resolves the cycle first.", - "checkOrderSpreadCycle.ts": "Spread cycle results depend on which checker resolves the cycle first.", - "checkOrderStableInstantiationErrors.ts": "Instantiation limit errors depend on which checker instantiates first.", -} - func (r *CompilerBaselineRunner) runSingleConfigTest(t *testing.T, testName string, test *compilerFileBasedTest, config *harnessutil.NamedTestConfiguration) { t.Parallel() - if !testutil.TestProgramIsSingleThreaded() { - if msg, ok := concurrentSkippedTests[tspath.GetBaseFileName(test.filename)]; ok { - t.Skipf("Skipping in concurrent mode: %s", msg) - } - } defer testutil.RecoverAndFail(t, "Panic on compiler test "+test.filename) payload := makeUnitsFromTest(test.content, test.filename) diff --git a/tsc/testdata/baselines/reference/compiler/bigintArbirtraryIdentifier(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/bigintArbirtraryIdentifier(checkorder=typesfirst).errors.txt index e70af53237c1f..5a959d4e04b62 100644 --- a/tsc/testdata/baselines/reference/compiler/bigintArbirtraryIdentifier(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/bigintArbirtraryIdentifier(checkorder=typesfirst).errors.txt @@ -6,7 +6,6 @@ badExport2.ts(1,16): error TS2304: Cannot find name 'foo'. badExport2.ts(1,20): error TS1128: Declaration or statement expected. badImport.ts(1,10): error TS1003: Identifier expected. badImport.ts(1,10): error TS1141: String literal expected. -badImport.ts(1,16): error TS2304: Cannot find name 'foo'. badImport.ts(1,20): error TS1128: Declaration or statement expected. badImport.ts(1,22): error TS1434: Unexpected keyword or identifier. badImport.ts(1,22): error TS2304: Cannot find name 'from'. @@ -25,14 +24,12 @@ badImport2.ts(1,22): error TS2304: Cannot find name 'from'. import { "0n" as foo } from "./foo"; export { foo as "0n" }; -==== badImport.ts (6 errors) ==== +==== badImport.ts (5 errors) ==== import { 0n as foo } from "./foo"; ~~ !!! error TS1003: Identifier expected. ~~~~~~~~~ !!! error TS1141: String literal expected. - ~~~ -!!! error TS2304: Cannot find name 'foo'. ~ !!! error TS1128: Declaration or statement expected. ~~~~ diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=default).errors.txt index fbdc7623ca157..126b9485926f5 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=default).errors.txt @@ -1,6 +1,7 @@ a.ts(5,11): error TS2322: Type 'string' is not assignable to type 'number'. a.ts(5,23): error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. b.ts(2,11): error TS2322: Type 'string' is not assignable to type 'number'. +b.ts(2,23): error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. ==== a.ts (2 errors) ==== @@ -15,10 +16,12 @@ b.ts(2,11): error TS2322: Type 'string' is not assignable to type 'number'. !!! error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. } -==== b.ts (1 errors) ==== +==== b.ts (2 errors) ==== async function f2() { const b: number = await s; ~ !!! error TS2322: Type 'string' is not assignable to type 'number'. + ~~~~~~~ +!!! error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=reverse).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=reverse).errors.txt index 55a3e6d752ab1..126b9485926f5 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=reverse).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=reverse).errors.txt @@ -1,9 +1,10 @@ a.ts(5,11): error TS2322: Type 'string' is not assignable to type 'number'. +a.ts(5,23): error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. b.ts(2,11): error TS2322: Type 'string' is not assignable to type 'number'. b.ts(2,23): error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. -==== a.ts (1 errors) ==== +==== a.ts (2 errors) ==== type S = string | Promise; declare const s: S; declare const p: Promise; @@ -11,6 +12,8 @@ b.ts(2,23): error TS1062: Type is referenced directly or indirectly in the fulfi const a: number = await p; ~ !!! error TS2322: Type 'string' is not assignable to type 'number'. + ~~~~~~~ +!!! error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. } ==== b.ts (2 errors) ==== diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=typesfirst).errors.txt index 55a3e6d752ab1..126b9485926f5 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderAwaitedCycle(checkorder=typesfirst).errors.txt @@ -1,9 +1,10 @@ a.ts(5,11): error TS2322: Type 'string' is not assignable to type 'number'. +a.ts(5,23): error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. b.ts(2,11): error TS2322: Type 'string' is not assignable to type 'number'. b.ts(2,23): error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. -==== a.ts (1 errors) ==== +==== a.ts (2 errors) ==== type S = string | Promise; declare const s: S; declare const p: Promise; @@ -11,6 +12,8 @@ b.ts(2,23): error TS1062: Type is referenced directly or indirectly in the fulfi const a: number = await p; ~ !!! error TS2322: Type 'string' is not assignable to type 'number'. + ~~~~~~~ +!!! error TS1062: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method. } ==== b.ts (2 errors) ==== diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=default).errors.txt index 938f4b7bb63d2..9309ae4c7eb15 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=default).errors.txt @@ -15,7 +15,6 @@ checkOrderCircularConstraint.ts(10,17): error TS2349: This expression is not cal class A { ~ !!! error TS2313: Type parameter 'T' has a circular constraint. -!!! related TS2751 checkOrderCircularConstraint.ts:7:17: Circularity originates in type at this location. foo() { var x!: T; var a = x.foo(); diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=reverse).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=reverse).errors.txt index 938f4b7bb63d2..9309ae4c7eb15 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=reverse).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=reverse).errors.txt @@ -15,7 +15,6 @@ checkOrderCircularConstraint.ts(10,17): error TS2349: This expression is not cal class A { ~ !!! error TS2313: Type parameter 'T' has a circular constraint. -!!! related TS2751 checkOrderCircularConstraint.ts:7:17: Circularity originates in type at this location. foo() { var x!: T; var a = x.foo(); diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=typesfirst).errors.txt index ef7991cfbe407..9309ae4c7eb15 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularConstraint(checkorder=typesfirst).errors.txt @@ -15,7 +15,6 @@ checkOrderCircularConstraint.ts(10,17): error TS2349: This expression is not cal class A { ~ !!! error TS2313: Type parameter 'T' has a circular constraint. -!!! related TS2751 checkOrderCircularConstraint.ts:10:17: Circularity originates in type at this location. foo() { var x!: T; var a = x.foo(); diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).errors.txt index 5c28d3ee0a214..4dee4171e029f 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).errors.txt @@ -1,15 +1,15 @@ -a.ts(2,24): error TS2716: Type parameter 'T' has a circular default. +b.ts(2,24): error TS2716: Type parameter 'U' has a circular default. -==== a.ts (1 errors) ==== +==== a.ts (0 errors) ==== import type { B } from "./b"; export interface A { a: T } - ~ -!!! error TS2716: Type parameter 'T' has a circular default. export declare const a: A; -==== b.ts (0 errors) ==== +==== b.ts (1 errors) ==== import type { A } from "./a"; export interface B { b: U } + ~ +!!! error TS2716: Type parameter 'U' has a circular default. export declare const b: B; \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).types b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).types index 145f91d78caf3..a88d998c374ec 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=default).types @@ -8,7 +8,7 @@ export interface A { a: T } >a : T export declare const a: A; ->a : A +>a : A> === b.ts === import type { A } from "./a"; @@ -18,5 +18,5 @@ export interface B { b: U } >b : U export declare const b: B; ->b : B> +>b : B diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).errors.txt new file mode 100644 index 0000000000000..4dee4171e029f --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).errors.txt @@ -0,0 +1,15 @@ +b.ts(2,24): error TS2716: Type parameter 'U' has a circular default. + + +==== a.ts (0 errors) ==== + import type { B } from "./b"; + export interface A { a: T } + export declare const a: A; + +==== b.ts (1 errors) ==== + import type { A } from "./a"; + export interface B { b: U } + ~ +!!! error TS2716: Type parameter 'U' has a circular default. + export declare const b: B; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).types b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).types index 145f91d78caf3..a88d998c374ec 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=isolated).types @@ -8,7 +8,7 @@ export interface A { a: T } >a : T export declare const a: A; ->a : A +>a : A> === b.ts === import type { A } from "./a"; @@ -18,5 +18,5 @@ export interface B { b: U } >b : U export declare const b: B; ->b : B> +>b : B diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).errors.txt index 5c28d3ee0a214..4dee4171e029f 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).errors.txt @@ -1,15 +1,15 @@ -a.ts(2,24): error TS2716: Type parameter 'T' has a circular default. +b.ts(2,24): error TS2716: Type parameter 'U' has a circular default. -==== a.ts (1 errors) ==== +==== a.ts (0 errors) ==== import type { B } from "./b"; export interface A { a: T } - ~ -!!! error TS2716: Type parameter 'T' has a circular default. export declare const a: A; -==== b.ts (0 errors) ==== +==== b.ts (1 errors) ==== import type { A } from "./a"; export interface B { b: U } + ~ +!!! error TS2716: Type parameter 'U' has a circular default. export declare const b: B; \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).types index 145f91d78caf3..a88d998c374ec 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderCircularDefaults(checkorder=typesfirst).types @@ -8,7 +8,7 @@ export interface A { a: T } >a : T export declare const a: A; ->a : A +>a : A> === b.ts === import type { A } from "./a"; @@ -18,5 +18,5 @@ export interface B { b: U } >b : U export declare const b: B; ->b : B> +>b : B diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=default).errors.txt index 0570bff3e21f0..81bd46c132406 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=default).errors.txt @@ -1,5 +1,5 @@ checkOrderEmitBeforeCheck.ts(9,5): error TS2502: '"each"' is referenced directly or indirectly in its own type annotation. -checkOrderEmitBeforeCheck.ts(15,1): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'. +checkOrderEmitBeforeCheck.ts(9,13): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ type: "type"; each: any; }'. ==== checkOrderEmitBeforeCheck.ts (2 errors) ==== @@ -14,12 +14,12 @@ checkOrderEmitBeforeCheck.ts(15,1): error TS2615: Type of property '"each"' circ "each": Child; ~~~~~~ !!! error TS2502: '"each"' is referenced directly or indirectly in its own type annotation. + ~~~~~~~~~~~~~~~~~ +!!! error TS2615: Type of property '"each"' circularly references itself in mapped type '{ type: "type"; each: any; }'. } type ListChild = Child declare let x: ListChild; x.type; - ~ -!!! error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=isolated).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=isolated).errors.txt index c220c7e1092af..81bd46c132406 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=isolated).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderEmitBeforeCheck(checkorder=isolated).errors.txt @@ -1,5 +1,5 @@ checkOrderEmitBeforeCheck.ts(9,5): error TS2502: '"each"' is referenced directly or indirectly in its own type annotation. -checkOrderEmitBeforeCheck.ts(9,13): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'. +checkOrderEmitBeforeCheck.ts(9,13): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ type: "type"; each: any; }'. ==== checkOrderEmitBeforeCheck.ts (2 errors) ==== @@ -15,7 +15,7 @@ checkOrderEmitBeforeCheck.ts(9,13): error TS2615: Type of property '"each"' circ ~~~~~~ !!! error TS2502: '"each"' is referenced directly or indirectly in its own type annotation. ~~~~~~~~~~~~~~~~~ -!!! error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'. +!!! error TS2615: Type of property '"each"' circularly references itself in mapped type '{ type: "type"; each: any; }'. } type ListChild = Child diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).js b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).js index 433d0bfbb69b5..8d70e46a3bf67 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).js +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).js @@ -57,7 +57,10 @@ module.exports = { v: { ...M } }; //// [xb.d.ts] declare const _exports: { - v: {}; + v: { + pa: {}; + ma: 1; + }; }; export = _exports; //// [mb.d.ts] @@ -65,10 +68,7 @@ export { v as pb } from "./xb"; export declare const mb = 1; //// [xa.d.ts] declare const _exports: { - v: { - pb: {}; - mb: 1; - }; + v: {}; }; export = _exports; //// [ma.d.ts] @@ -77,10 +77,7 @@ export declare const ma = 1; //// [ia.d.ts] declare const _exports: { v: { - pa: { - pb: {}; - mb: 1; - }; + pa: {}; ma: 1; }; }; diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).types b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).types index 216ad9304dfd7..b82d88007b600 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=default).types @@ -7,19 +7,19 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } ->exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") === /ma.js === export { v as pa } from "./xa"; ->v : { pb: {}; mb: 1; } ->pa : { pb: {}; mb: 1; } +>v : {} +>pa : {} export const ma = 1; >ma : 1 @@ -27,8 +27,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : {} ->pb : {} +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } export const mb = 1; >mb : 1 @@ -41,13 +41,13 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>module.exports = { v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") === /xb.js === @@ -57,12 +57,12 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).js b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).js index 8621be455c50d..8d70e46a3bf67 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).js +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).js @@ -56,13 +56,20 @@ module.exports = { v: { ...M } }; //// [xb.d.ts] -declare const _exports: any; +declare const _exports: { + v: { + pa: {}; + ma: 1; + }; +}; export = _exports; //// [mb.d.ts] export { v as pb } from "./xb"; export declare const mb = 1; //// [xa.d.ts] -declare const _exports: any; +declare const _exports: { + v: {}; +}; export = _exports; //// [ma.d.ts] export { v as pa } from "./xa"; @@ -70,10 +77,7 @@ export declare const ma = 1; //// [ia.d.ts] declare const _exports: { v: { - pa: { - pb: {}; - mb: 1; - }; + pa: {}; ma: 1; }; }; diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).types b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).types index 216ad9304dfd7..b82d88007b600 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=isolated).types @@ -7,19 +7,19 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } ->exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") === /ma.js === export { v as pa } from "./xa"; ->v : { pb: {}; mb: 1; } ->pa : { pb: {}; mb: 1; } +>v : {} +>pa : {} export const ma = 1; >ma : 1 @@ -27,8 +27,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : {} ->pb : {} +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } export const mb = 1; >mb : 1 @@ -41,13 +41,13 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>module.exports = { v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") === /xb.js === @@ -57,12 +57,12 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).js b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).js index 433d0bfbb69b5..8d70e46a3bf67 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).js +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).js @@ -57,7 +57,10 @@ module.exports = { v: { ...M } }; //// [xb.d.ts] declare const _exports: { - v: {}; + v: { + pa: {}; + ma: 1; + }; }; export = _exports; //// [mb.d.ts] @@ -65,10 +68,7 @@ export { v as pb } from "./xb"; export declare const mb = 1; //// [xa.d.ts] declare const _exports: { - v: { - pb: {}; - mb: 1; - }; + v: {}; }; export = _exports; //// [ma.d.ts] @@ -77,10 +77,7 @@ export declare const ma = 1; //// [ia.d.ts] declare const _exports: { v: { - pa: { - pb: {}; - mb: 1; - }; + pa: {}; ma: 1; }; }; diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).types b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).types index 216ad9304dfd7..b82d88007b600 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=reverse).types @@ -7,19 +7,19 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } ->exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") === /ma.js === export { v as pa } from "./xa"; ->v : { pb: {}; mb: 1; } ->pa : { pb: {}; mb: 1; } +>v : {} +>pa : {} export const ma = 1; >ma : 1 @@ -27,8 +27,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : {} ->pb : {} +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } export const mb = 1; >mb : 1 @@ -41,13 +41,13 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>module.exports = { v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") === /xb.js === @@ -57,12 +57,12 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).js b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).js index 433d0bfbb69b5..8d70e46a3bf67 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).js +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).js @@ -57,7 +57,10 @@ module.exports = { v: { ...M } }; //// [xb.d.ts] declare const _exports: { - v: {}; + v: { + pa: {}; + ma: 1; + }; }; export = _exports; //// [mb.d.ts] @@ -65,10 +68,7 @@ export { v as pb } from "./xb"; export declare const mb = 1; //// [xa.d.ts] declare const _exports: { - v: { - pb: {}; - mb: 1; - }; + v: {}; }; export = _exports; //// [ma.d.ts] @@ -77,10 +77,7 @@ export declare const ma = 1; //// [ia.d.ts] declare const _exports: { v: { - pa: { - pb: {}; - mb: 1; - }; + pa: {}; ma: 1; }; }; diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).types index 216ad9304dfd7..b82d88007b600 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/checkOrderSpreadCycle(checkorder=typesfirst).types @@ -7,19 +7,19 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } ->exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") === /ma.js === export { v as pa } from "./xa"; ->v : { pb: {}; mb: 1; } ->pa : { pb: {}; mb: 1; } +>v : {} +>pa : {} export const ma = 1; >ma : 1 @@ -27,8 +27,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : {} ->pb : {} +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } export const mb = 1; >mb : 1 @@ -41,13 +41,13 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>module.exports = { v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") === /xb.js === @@ -57,12 +57,12 @@ const M = /** @type {any} */ (null); >(null) : any module.exports = { v: { ...M } }; ->module.exports = { v: { ...M } } : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>module.exports = { v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=default).errors.txt index 735d4e676a17d..a547413430b10 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=default).errors.txt @@ -1,4 +1,7 @@ +a.ts(2,12): error TS2589: Type instantiation is excessively deep and possibly infinite. a.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly infinite. +b.ts(2,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +b.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly infinite. ==== lib.ts (0 errors) ==== @@ -9,15 +12,21 @@ a.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly in export type Recur = N extends 0 ? S : Recur, `${S}_${S}`>; -==== a.ts (1 errors) ==== +==== a.ts (2 errors) ==== import { Recur } from "./lib"; export let a: Recur<5, "a">; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~~~~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. -==== b.ts (0 errors) ==== +==== b.ts (2 errors) ==== import { Recur } from "./lib"; export let b: Recur<5, "a">; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ==== c.ts (0 errors) ==== class T { diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=reverse).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=reverse).errors.txt index 6e370b78d30f9..a547413430b10 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=reverse).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=reverse).errors.txt @@ -1,3 +1,6 @@ +a.ts(2,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +a.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly infinite. +b.ts(2,12): error TS2589: Type instantiation is excessively deep and possibly infinite. b.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly infinite. @@ -9,13 +12,19 @@ b.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly in export type Recur = N extends 0 ? S : Recur, `${S}_${S}`>; -==== a.ts (0 errors) ==== +==== a.ts (2 errors) ==== import { Recur } from "./lib"; export let a: Recur<5, "a">; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. -==== b.ts (1 errors) ==== +==== b.ts (2 errors) ==== import { Recur } from "./lib"; export let b: Recur<5, "a">; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~~~~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. diff --git a/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=typesfirst).errors.txt index 9b99a0b62969b..a547413430b10 100644 --- a/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/checkOrderStableInstantiationErrors(checkorder=typesfirst).errors.txt @@ -1,7 +1,9 @@ -error TS2589: Type instantiation is excessively deep and possibly infinite. +a.ts(2,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +a.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly infinite. +b.ts(2,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +b.ts(2,15): error TS2589: Type instantiation is excessively deep and possibly infinite. -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ==== lib.ts (0 errors) ==== export type Dec = N extends 5 ? 4 : N extends 4 ? 3 : N extends 3 ? 2 : @@ -10,13 +12,21 @@ error TS2589: Type instantiation is excessively deep and possibly infinite. export type Recur = N extends 0 ? S : Recur, `${S}_${S}`>; -==== a.ts (0 errors) ==== +==== a.ts (2 errors) ==== import { Recur } from "./lib"; export let a: Recur<5, "a">; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. -==== b.ts (0 errors) ==== +==== b.ts (2 errors) ==== import { Recur } from "./lib"; export let b: Recur<5, "a">; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ==== c.ts (0 errors) ==== class T { diff --git a/tsc/testdata/baselines/reference/compiler/circularAccessorAnnotations(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/circularAccessorAnnotations(checkorder=typesfirst).types index e6b4d1fca90ca..2b01358a33dca 100644 --- a/tsc/testdata/baselines/reference/compiler/circularAccessorAnnotations(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/circularAccessorAnnotations(checkorder=typesfirst).types @@ -2,23 +2,23 @@ === circularAccessorAnnotations.ts === declare const c1: { ->c1 : { readonly foo: any; } +>c1 : { readonly foo: typeof c1.foo; } get foo(): typeof c1.foo; >foo : any >c1.foo : any ->c1 : { readonly foo: any; } +>c1 : { readonly foo: typeof c1.foo; } >foo : any } declare const c2: { ->c2 : { foo: any; } +>c2 : { foo: typeof c2.foo; } set foo(value: typeof c2.foo); >foo : any >value : any >c2.foo : any ->c2 : { foo: any; } +>c2 : { foo: typeof c2.foo; } >foo : any } diff --git a/tsc/testdata/baselines/reference/compiler/circularIndexedPropertyInstantiation.errors.txt b/tsc/testdata/baselines/reference/compiler/circularIndexedPropertyInstantiation.errors.txt index 3322a7ea25b66..1d225438007a0 100644 --- a/tsc/testdata/baselines/reference/compiler/circularIndexedPropertyInstantiation.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/circularIndexedPropertyInstantiation.errors.txt @@ -1,12 +1,15 @@ +circularIndexedPropertyInstantiation.ts(4,5): error TS5114: Instantiations of type 'Foo' appear infinitely circular. circularIndexedPropertyInstantiation.ts(4,9): error TS5114: Instantiations of type 'Foo' appear infinitely circular. circularIndexedPropertyInstantiation.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'. -==== circularIndexedPropertyInstantiation.ts (2 errors) ==== +==== circularIndexedPropertyInstantiation.ts (3 errors) ==== // Repro from #14837 type Foo = { "true": Foo> }[T]; let f1: Foo<"true", {}>; + ~~ +!!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. ~~~~~~~~~~~~~~~ !!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. let f2: Foo<"false", {}>; diff --git a/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=default).errors.txt index 8d568d19a5373..61ac052a41c80 100644 --- a/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=default).errors.txt @@ -1,7 +1,8 @@ circularInlineMappedGenericTupleTypeNoCrash.ts(11,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +circularInlineMappedGenericTupleTypeNoCrash.ts(11,60): error TS2589: Type instantiation is excessively deep and possibly infinite. -==== circularInlineMappedGenericTupleTypeNoCrash.ts (1 errors) ==== +==== circularInlineMappedGenericTupleTypeNoCrash.ts (2 errors) ==== class Foo { public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } }; @@ -14,6 +15,8 @@ circularInlineMappedGenericTupleTypeNoCrash.ts(11,12): error TS2589: Type instan public add(): Foo<[...Elements, "abc"]> { return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" }); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. } } diff --git a/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=typesfirst).errors.txt index 8d568d19a5373..61ac052a41c80 100644 --- a/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/circularInlineMappedGenericTupleTypeNoCrash(checkorder=typesfirst).errors.txt @@ -1,7 +1,8 @@ circularInlineMappedGenericTupleTypeNoCrash.ts(11,12): error TS2589: Type instantiation is excessively deep and possibly infinite. +circularInlineMappedGenericTupleTypeNoCrash.ts(11,60): error TS2589: Type instantiation is excessively deep and possibly infinite. -==== circularInlineMappedGenericTupleTypeNoCrash.ts (1 errors) ==== +==== circularInlineMappedGenericTupleTypeNoCrash.ts (2 errors) ==== class Foo { public readonly elements: { [P in keyof Elements]: { bar: Elements[P] } }; @@ -14,6 +15,8 @@ circularInlineMappedGenericTupleTypeNoCrash.ts(11,12): error TS2589: Type instan public add(): Foo<[...Elements, "abc"]> { return new Foo<[...Elements, "abc"]>(...this.elements, { bar: "abc" }); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. } } diff --git a/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.errors.txt index f0b2f978c0228..5f36a6dd6ba9d 100644 --- a/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.errors.txt @@ -1,10 +1,14 @@ +declaration.ts(6,5): error TS7023: 'rebuild' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +declaration.ts(9,5): error TS7023: 'b' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. expression.ts(10,7): error TS2322: Type 'typeof A' is not assignable to type 'new () => A'. Type 'A' is not assignable to type 'A'. Two different types with this name exist, but they are unrelated. Types of property 'c' are incompatible. - Type '() => () => A | A' is not assignable to type 'A'. + Type '() => () => any' is not assignable to type 'A'. +expression.ts(11,5): error TS7023: 'rebuild' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. +expression.ts(14,5): error TS7023: 'b' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. -==== expression.ts (1 errors) ==== +==== expression.ts (3 errors) ==== // Subtype reduction of rebuild's return type relates the class to A, // which needs b's return type, which needs rebuild's: a cycle. Both // members report it, whichever return type is requested first, as if @@ -19,11 +23,15 @@ expression.ts(10,7): error TS2322: Type 'typeof A' is not assignable to type 'ne !!! error TS2322: Type 'typeof A' is not assignable to type 'new () => A'. !!! error TS2322: Type 'A' is not assignable to type 'A'. Two different types with this name exist, but they are unrelated. !!! error TS2322: Types of property 'c' are incompatible. -!!! error TS2322: Type '() => () => A | A' is not assignable to type 'A'. +!!! error TS2322: Type '() => () => any' is not assignable to type 'A'. rebuild() { + ~~~~~~~ +!!! error TS7023: 'rebuild' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. return flag ? this : new A(); } b() { + ~ +!!! error TS7023: 'b' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. return this.rebuild(); } c() { @@ -32,16 +40,20 @@ expression.ts(10,7): error TS2322: Type 'typeof A' is not assignable to type 'ne }; export {}; -==== declaration.ts (0 errors) ==== +==== declaration.ts (2 errors) ==== interface B { b(): B; } declare const flag: boolean; class K { rebuild() { + ~~~~~~~ +!!! error TS7023: 'rebuild' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. return flag ? this : new B(); } b() { + ~ +!!! error TS7023: 'b' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. return this.rebuild(); } } diff --git a/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.types b/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.types index 9088517456379..421302078810c 100644 --- a/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.types +++ b/tsc/testdata/baselines/reference/compiler/circularReturnTypeSubtypeReductionOrder.types @@ -20,7 +20,7 @@ const A: new () => A = class { >class { rebuild() { return flag ? this : new A(); } b() { return this.rebuild(); } c() { return this.rebuild; }} : typeof A rebuild() { ->rebuild : () => this | A +>rebuild : () => any return flag ? this : new A(); >flag ? this : new A() : this | A @@ -30,21 +30,21 @@ const A: new () => A = class { >A : new () => A } b() { ->b : () => this | A +>b : () => any return this.rebuild(); ->this.rebuild() : this | A ->this.rebuild : () => this | A +>this.rebuild() : any +>this.rebuild : () => any >this : this ->rebuild : () => this | A +>rebuild : () => any } c() { ->c : () => () => this | A +>c : () => () => any return this.rebuild; ->this.rebuild : () => this | A +>this.rebuild : () => any >this : this ->rebuild : () => this | A +>rebuild : () => any } }; export {}; @@ -61,7 +61,7 @@ class K { >K : K rebuild() { ->rebuild : () => this | B +>rebuild : () => any return flag ? this : new B(); >flag ? this : new B() : this | B @@ -71,13 +71,13 @@ class K { >B : new () => B } b() { ->b : () => this | B +>b : () => any return this.rebuild(); ->this.rebuild() : this | B ->this.rebuild : () => this | B +>this.rebuild() : any +>this.rebuild : () => any >this : this ->rebuild : () => this | B +>rebuild : () => any } } const B: new () => B = K; diff --git a/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=default).types b/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=default).types index 41f30b7dbe3f2..f1af064690eee 100644 --- a/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=default).types +++ b/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=default).types @@ -27,8 +27,8 @@ declare function type(x: T): Type; >x : T export const $ = scope({ ->$ : Scope<{ a: Type; b: unknown; }> ->scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $.type("a").pipe(function (n) { return n === 0; }),}) : Scope<{ a: Type; b: unknown; }> +>$ : Scope<{ a: Type; b: () => Type; }> +>scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $.type("a").pipe(function (n) { return n === 0; }),}) : Scope<{ readonly a: Type; readonly b: () => Type; }> >scope : (def: Validate) => Scope >{ a: type("string").pipe(function (s) { return s.length; }), b: () => $.type("a").pipe(function (n) { return n === 0; }),} : { a: Type; b: () => Type; } @@ -52,9 +52,9 @@ export const $ = scope({ >$.type("a").pipe(function (n) { return n === 0; }) : Type >$.type("a").pipe : (f: (x: number) => U) => Type >$.type("a") : Type ->$.type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> ->$ : Scope<{ a: Type; b: unknown; }> ->type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> +>$.type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> +>$ : Scope<{ a: Type; b: () => Type; }> +>type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> >"a" : "a" >pipe : (f: (x: number) => U) => Type >function (n) { return n === 0; } : (n: number) => n is 0 @@ -67,8 +67,8 @@ export const $ = scope({ // Entered from the function in the first property when queried in reverse export const $2 = scope({ ->$2 : Scope<{ a: Type; b: unknown; }> ->scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $2.type("a").pipe((n: number) => n === 0),}) : Scope<{ a: Type; b: unknown; }> +>$2 : Scope<{ a: Type; b: () => Type; }> +>scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $2.type("a").pipe((n: number) => n === 0),}) : Scope<{ readonly a: Type; readonly b: () => Type; }> >scope : (def: Validate) => Scope >{ a: type("string").pipe(function (s) { return s.length; }), b: () => $2.type("a").pipe((n: number) => n === 0),} : { a: Type; b: () => Type; } @@ -92,9 +92,9 @@ export const $2 = scope({ >$2.type("a").pipe((n: number) => n === 0) : Type >$2.type("a").pipe : (f: (x: number) => U) => Type >$2.type("a") : Type ->$2.type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> ->$2 : Scope<{ a: Type; b: unknown; }> ->type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> +>$2.type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> +>$2 : Scope<{ a: Type; b: () => Type; }> +>type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> >"a" : "a" >pipe : (f: (x: number) => U) => Type >(n: number) => n === 0 : (n: number) => n is 0 diff --git a/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=typesfirst).types index 41f30b7dbe3f2..f1af064690eee 100644 --- a/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/circularScopeThunkReverseMapped(checkorder=typesfirst).types @@ -27,8 +27,8 @@ declare function type(x: T): Type; >x : T export const $ = scope({ ->$ : Scope<{ a: Type; b: unknown; }> ->scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $.type("a").pipe(function (n) { return n === 0; }),}) : Scope<{ a: Type; b: unknown; }> +>$ : Scope<{ a: Type; b: () => Type; }> +>scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $.type("a").pipe(function (n) { return n === 0; }),}) : Scope<{ readonly a: Type; readonly b: () => Type; }> >scope : (def: Validate) => Scope >{ a: type("string").pipe(function (s) { return s.length; }), b: () => $.type("a").pipe(function (n) { return n === 0; }),} : { a: Type; b: () => Type; } @@ -52,9 +52,9 @@ export const $ = scope({ >$.type("a").pipe(function (n) { return n === 0; }) : Type >$.type("a").pipe : (f: (x: number) => U) => Type >$.type("a") : Type ->$.type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> ->$ : Scope<{ a: Type; b: unknown; }> ->type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> +>$.type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> +>$ : Scope<{ a: Type; b: () => Type; }> +>type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> >"a" : "a" >pipe : (f: (x: number) => U) => Type >function (n) { return n === 0; } : (n: number) => n is 0 @@ -67,8 +67,8 @@ export const $ = scope({ // Entered from the function in the first property when queried in reverse export const $2 = scope({ ->$2 : Scope<{ a: Type; b: unknown; }> ->scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $2.type("a").pipe((n: number) => n === 0),}) : Scope<{ a: Type; b: unknown; }> +>$2 : Scope<{ a: Type; b: () => Type; }> +>scope({ a: type("string").pipe(function (s) { return s.length; }), b: () => $2.type("a").pipe((n: number) => n === 0),}) : Scope<{ readonly a: Type; readonly b: () => Type; }> >scope : (def: Validate) => Scope >{ a: type("string").pipe(function (s) { return s.length; }), b: () => $2.type("a").pipe((n: number) => n === 0),} : { a: Type; b: () => Type; } @@ -92,9 +92,9 @@ export const $2 = scope({ >$2.type("a").pipe((n: number) => n === 0) : Type >$2.type("a").pipe : (f: (x: number) => U) => Type >$2.type("a") : Type ->$2.type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> ->$2 : Scope<{ a: Type; b: unknown; }> ->type : (k: k_1) => Resolve<{ a: Type; b: unknown; }[k_1]> +>$2.type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> +>$2 : Scope<{ a: Type; b: () => Type; }> +>type : (k: k_1) => Resolve<{ a: Type; b: () => Type; }[k_1]> >"a" : "a" >pipe : (f: (x: number) => U) => Type >(n: number) => n === 0 : (n: number) => n is 0 diff --git a/tsc/testdata/baselines/reference/compiler/classExpressionWithDecorator1.errors.txt b/tsc/testdata/baselines/reference/compiler/classExpressionWithDecorator1.errors.txt index 1d2d3f5568b0f..9a749f4c13ab3 100644 --- a/tsc/testdata/baselines/reference/compiler/classExpressionWithDecorator1.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/classExpressionWithDecorator1.errors.txt @@ -1,7 +1,10 @@ classExpressionWithDecorator1.ts(1,9): error TS1206: Decorators are not valid here. +classExpressionWithDecorator1.ts(1,10): error TS2304: Cannot find name 'decorate'. -==== classExpressionWithDecorator1.ts (1 errors) ==== +==== classExpressionWithDecorator1.ts (2 errors) ==== var v = @decorate class C { static p = 1 }; ~ -!!! error TS1206: Decorators are not valid here. \ No newline at end of file +!!! error TS1206: Decorators are not valid here. + ~~~~~~~~ +!!! error TS2304: Cannot find name 'decorate'. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.errors.txt b/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.errors.txt deleted file mode 100644 index 502975549c191..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.errors.txt +++ /dev/null @@ -1,24 +0,0 @@ -classVarianceResolveCircularity2.ts(7,5): error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. - - -==== classVarianceResolveCircularity2.ts (1 errors) ==== - // Issue #52813 - - export {}; - - class Bar { - num!: number; - Value = callme(new Foo(this)).bar.num; - ~~~~~ -!!! error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. - Field: number = callme(new Foo(this)).bar.num; - } - declare function callme(x: Foo): Foo; - declare function callme(x: object): string; - - class Foo { - bar!: Bar; - constructor(bar: Bar) { - this.bar = bar; - } - } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.types b/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.types index d1bd8c01269ff..1aa4a1481a92c 100644 --- a/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.types +++ b/tsc/testdata/baselines/reference/compiler/classVarianceResolveCircularity2.types @@ -12,7 +12,7 @@ class Bar { >num : number Value = callme(new Foo(this)).bar.num; ->Value : any +>Value : number >callme(new Foo(this)).bar.num : number >callme(new Foo(this)).bar : Bar >callme(new Foo(this)) : Foo diff --git a/tsc/testdata/baselines/reference/compiler/constructorWithIncompleteTypeAnnotation.errors.txt b/tsc/testdata/baselines/reference/compiler/constructorWithIncompleteTypeAnnotation.errors.txt index 81ac42365af10..2852054f9325f 100644 --- a/tsc/testdata/baselines/reference/compiler/constructorWithIncompleteTypeAnnotation.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/constructorWithIncompleteTypeAnnotation.errors.txt @@ -1,5 +1,4 @@ constructorWithIncompleteTypeAnnotation.ts(11,13): error TS2503: Cannot find namespace 'module'. -constructorWithIncompleteTypeAnnotation.ts(11,13): error TS2591: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig. constructorWithIncompleteTypeAnnotation.ts(11,19): error TS1005: ';' expected. constructorWithIncompleteTypeAnnotation.ts(22,35): error TS1005: ')' expected. constructorWithIncompleteTypeAnnotation.ts(22,39): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. @@ -95,7 +94,7 @@ constructorWithIncompleteTypeAnnotation.ts(259,55): error TS1005: ';' expected. constructorWithIncompleteTypeAnnotation.ts(261,1): error TS1128: Declaration or statement expected. -==== constructorWithIncompleteTypeAnnotation.ts (95 errors) ==== +==== constructorWithIncompleteTypeAnnotation.ts (94 errors) ==== declare module "fs" { export class File { constructor(filename: string); @@ -109,8 +108,6 @@ constructorWithIncompleteTypeAnnotation.ts(261,1): error TS1128: Declaration or import fs = module("fs"); ~~~~~~ !!! error TS2503: Cannot find namespace 'module'. - ~~~~~~ -!!! error TS2591: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig. ~ !!! error TS1005: ';' expected. diff --git a/tsc/testdata/baselines/reference/compiler/contextualCheckInDiscardedCycle.symbols b/tsc/testdata/baselines/reference/compiler/contextualCheckInDiscardedCycle.symbols new file mode 100644 index 0000000000000..3169a3c3cbd08 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualCheckInDiscardedCycle.symbols @@ -0,0 +1,150 @@ +//// [tests/cases/compiler/contextualCheckInDiscardedCycle.ts] //// + +=== returnTypeCycle.ts === +// Callbacks contextually checked while a cycle is in progress are checked +// again once it is discarded, with their contextual parameter types. + +declare function useCallback(callback: T): T; +>useCallback : Symbol(useCallback, Decl(returnTypeCycle.ts, 0, 0)) +>T : Symbol(T, Decl(returnTypeCycle.ts, 3, 29)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.esnext.decorators.d.ts, --, --)) +>callback : Symbol(callback, Decl(returnTypeCycle.ts, 3, 49)) +>T : Symbol(T, Decl(returnTypeCycle.ts, 3, 29)) +>T : Symbol(T, Decl(returnTypeCycle.ts, 3, 29)) + +declare function refreshAll(options: { ids: string[]; extra: T }): Promise; +>refreshAll : Symbol(refreshAll, Decl(returnTypeCycle.ts, 3, 65)) +>T : Symbol(T, Decl(returnTypeCycle.ts, 4, 28)) +>options : Symbol(options, Decl(returnTypeCycle.ts, 4, 31)) +>ids : Symbol(ids, Decl(returnTypeCycle.ts, 4, 41)) +>extra : Symbol(extra, Decl(returnTypeCycle.ts, 4, 56)) +>T : Symbol(T, Decl(returnTypeCycle.ts, 4, 28)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>T : Symbol(T, Decl(returnTypeCycle.ts, 4, 28)) + +interface View { +>View : Symbol(View, Decl(returnTypeCycle.ts, 4, 81)) + + refresh: (force?: boolean) => Promise; +>refresh : Symbol(View.refresh, Decl(returnTypeCycle.ts, 6, 16)) +>force : Symbol(force, Decl(returnTypeCycle.ts, 7, 14)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +} + +declare const items: { id: string }[]; +>items : Symbol(items, Decl(returnTypeCycle.ts, 10, 13)) +>id : Symbol(id, Decl(returnTypeCycle.ts, 10, 22)) + +function useView(): View { +>useView : Symbol(useView, Decl(returnTypeCycle.ts, 10, 38)) +>View : Symbol(View, Decl(returnTypeCycle.ts, 4, 81)) + + const refresh = useCallback((force?: boolean) => +>refresh : Symbol(refresh, Decl(returnTypeCycle.ts, 13, 9)) +>useCallback : Symbol(useCallback, Decl(returnTypeCycle.ts, 0, 0)) +>force : Symbol(force, Decl(returnTypeCycle.ts, 13, 33)) + + refreshAll({ +>refreshAll : Symbol(refreshAll, Decl(returnTypeCycle.ts, 3, 65)) + + ids: items.map(({ id }) => id), +>ids : Symbol(ids, Decl(returnTypeCycle.ts, 14, 20)) +>items.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>items : Symbol(items, Decl(returnTypeCycle.ts, 10, 13)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>id : Symbol(id, Decl(returnTypeCycle.ts, 15, 29)) +>id : Symbol(id, Decl(returnTypeCycle.ts, 15, 29)) + + extra: 1, +>extra : Symbol(extra, Decl(returnTypeCycle.ts, 15, 43)) + + })); + return { refresh }; +>refresh : Symbol(refresh, Decl(returnTypeCycle.ts, 18, 12)) +} + +=== yieldOperandCycle.ts === +interface Eff { +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 0, 14)) +>E : Symbol(E, Decl(yieldOperandCycle.ts, 0, 16)) + + [Symbol.iterator](): Generator, A, any>; +>[Symbol.iterator] : Symbol(Eff[Symbol.iterator], Decl(yieldOperandCycle.ts, 0, 21)) +>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 0, 14)) +>E : Symbol(E, Decl(yieldOperandCycle.ts, 0, 16)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 0, 14)) + + pipe(f: (self: Eff) => B): B; +>pipe : Symbol(Eff.pipe, Decl(yieldOperandCycle.ts, 1, 54)) +>B : Symbol(B, Decl(yieldOperandCycle.ts, 2, 9)) +>f : Symbol(f, Decl(yieldOperandCycle.ts, 2, 12)) +>self : Symbol(self, Decl(yieldOperandCycle.ts, 2, 16)) +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 0, 14)) +>E : Symbol(E, Decl(yieldOperandCycle.ts, 0, 16)) +>B : Symbol(B, Decl(yieldOperandCycle.ts, 2, 9)) +>B : Symbol(B, Decl(yieldOperandCycle.ts, 2, 9)) +} +declare function mapError(f: (e: E) => E2): (self: Eff) => Eff; +>mapError : Symbol(mapError, Decl(yieldOperandCycle.ts, 3, 1)) +>E : Symbol(E, Decl(yieldOperandCycle.ts, 4, 26)) +>E2 : Symbol(E2, Decl(yieldOperandCycle.ts, 4, 28)) +>f : Symbol(f, Decl(yieldOperandCycle.ts, 4, 33)) +>e : Symbol(e, Decl(yieldOperandCycle.ts, 4, 37)) +>E : Symbol(E, Decl(yieldOperandCycle.ts, 4, 26)) +>E2 : Symbol(E2, Decl(yieldOperandCycle.ts, 4, 28)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 4, 52)) +>self : Symbol(self, Decl(yieldOperandCycle.ts, 4, 55)) +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 4, 52)) +>E : Symbol(E, Decl(yieldOperandCycle.ts, 4, 26)) +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>A : Symbol(A, Decl(yieldOperandCycle.ts, 4, 52)) +>E2 : Symbol(E2, Decl(yieldOperandCycle.ts, 4, 28)) + +declare function fn(body: (input: number) => Generator): (input: number) => Eff; +>fn : Symbol(fn, Decl(yieldOperandCycle.ts, 4, 86)) +>Y : Symbol(Y, Decl(yieldOperandCycle.ts, 5, 20)) +>R : Symbol(R, Decl(yieldOperandCycle.ts, 5, 22)) +>body : Symbol(body, Decl(yieldOperandCycle.ts, 5, 26)) +>input : Symbol(input, Decl(yieldOperandCycle.ts, 5, 33)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>Y : Symbol(Y, Decl(yieldOperandCycle.ts, 5, 20)) +>R : Symbol(R, Decl(yieldOperandCycle.ts, 5, 22)) +>input : Symbol(input, Decl(yieldOperandCycle.ts, 5, 75)) +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>R : Symbol(R, Decl(yieldOperandCycle.ts, 5, 22)) + +declare function make(s: T): T; +>make : Symbol(make, Decl(yieldOperandCycle.ts, 5, 107)) +>T : Symbol(T, Decl(yieldOperandCycle.ts, 6, 22)) +>s : Symbol(s, Decl(yieldOperandCycle.ts, 6, 40)) +>T : Symbol(T, Decl(yieldOperandCycle.ts, 6, 22)) +>T : Symbol(T, Decl(yieldOperandCycle.ts, 6, 22)) + +declare const uuid: Eff; +>uuid : Symbol(uuid, Decl(yieldOperandCycle.ts, 7, 13)) +>Eff : Symbol(Eff, Decl(yieldOperandCycle.ts, 0, 0)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + +fn(function* (input) { +>fn : Symbol(fn, Decl(yieldOperandCycle.ts, 4, 86)) +>input : Symbol(input, Decl(yieldOperandCycle.ts, 9, 14)) + + make(yield* uuid.pipe(mapError((cause) => ({ cause })))); +>make : Symbol(make, Decl(yieldOperandCycle.ts, 5, 107)) +>uuid.pipe : Symbol(Eff.pipe, Decl(yieldOperandCycle.ts, 1, 54)) +>uuid : Symbol(uuid, Decl(yieldOperandCycle.ts, 7, 13)) +>pipe : Symbol(Eff.pipe, Decl(yieldOperandCycle.ts, 1, 54)) +>mapError : Symbol(mapError, Decl(yieldOperandCycle.ts, 3, 1)) +>cause : Symbol(cause, Decl(yieldOperandCycle.ts, 10, 36)) +>cause : Symbol(cause, Decl(yieldOperandCycle.ts, 10, 48)) + +}); + diff --git a/tsc/testdata/baselines/reference/compiler/contextualCheckInDiscardedCycle.types b/tsc/testdata/baselines/reference/compiler/contextualCheckInDiscardedCycle.types new file mode 100644 index 0000000000000..7506ea748391e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualCheckInDiscardedCycle.types @@ -0,0 +1,117 @@ +//// [tests/cases/compiler/contextualCheckInDiscardedCycle.ts] //// + +=== returnTypeCycle.ts === +// Callbacks contextually checked while a cycle is in progress are checked +// again once it is discarded, with their contextual parameter types. + +declare function useCallback(callback: T): T; +>useCallback : (callback: T) => T +>callback : T + +declare function refreshAll(options: { ids: string[]; extra: T }): Promise; +>refreshAll : (options: { ids: string[]; extra: T; }) => Promise +>options : { ids: string[]; extra: T; } +>ids : string[] +>extra : T + +interface View { + refresh: (force?: boolean) => Promise; +>refresh : (force?: boolean) => Promise +>force : boolean | undefined +} + +declare const items: { id: string }[]; +>items : { id: string; }[] +>id : string + +function useView(): View { +>useView : () => View + + const refresh = useCallback((force?: boolean) => +>refresh : (force?: boolean) => Promise +>useCallback((force?: boolean) => refreshAll({ ids: items.map(({ id }) => id), extra: 1, })) : (force?: boolean) => Promise +>useCallback : (callback: T) => T +>(force?: boolean) => refreshAll({ ids: items.map(({ id }) => id), extra: 1, }) : (force?: boolean) => Promise +>force : boolean | undefined + + refreshAll({ +>refreshAll({ ids: items.map(({ id }) => id), extra: 1, }) : Promise +>refreshAll : (options: { ids: string[]; extra: T; }) => Promise +>{ ids: items.map(({ id }) => id), extra: 1, } : { ids: string[]; extra: number; } + + ids: items.map(({ id }) => id), +>ids : string[] +>items.map(({ id }) => id) : string[] +>items.map : (callbackfn: (value: { id: string; }, index: number, array: { id: string; }[]) => U, thisArg?: any) => U[] +>items : { id: string; }[] +>map : (callbackfn: (value: { id: string; }, index: number, array: { id: string; }[]) => U, thisArg?: any) => U[] +>({ id }) => id : ({ id }: { id: string; }) => string +>id : string +>id : string + + extra: 1, +>extra : number +>1 : 1 + + })); + return { refresh }; +>{ refresh } : { refresh: (force?: boolean) => Promise; } +>refresh : (force?: boolean) => Promise +} + +=== yieldOperandCycle.ts === +interface Eff { + [Symbol.iterator](): Generator, A, any>; +>[Symbol.iterator] : () => Generator, A, any> +>Symbol.iterator : unique symbol +>Symbol : SymbolConstructor +>iterator : unique symbol + + pipe(f: (self: Eff) => B): B; +>pipe : (f: (self: Eff) => B) => B +>f : (self: Eff) => B +>self : Eff +} +declare function mapError(f: (e: E) => E2): (self: Eff) => Eff; +>mapError : (f: (e: E) => E2) => (self: Eff) => Eff +>f : (e: E) => E2 +>e : E +>self : Eff + +declare function fn(body: (input: number) => Generator): (input: number) => Eff; +>fn : (body: (input: number) => Generator) => (input: number) => Eff +>body : (input: number) => Generator +>input : number +>input : number + +declare function make(s: T): T; +>make : (s: T) => T +>s : T + +declare const uuid: Eff; +>uuid : Eff + +fn(function* (input) { +>fn(function* (input) { make(yield* uuid.pipe(mapError((cause) => ({ cause }))));}) : (input: number) => Eff +>fn : (body: (input: number) => Generator) => (input: number) => Eff +>function* (input) { make(yield* uuid.pipe(mapError((cause) => ({ cause }))));} : (input: number) => Generator, void, any> +>input : number + + make(yield* uuid.pipe(mapError((cause) => ({ cause })))); +>make(yield* uuid.pipe(mapError((cause) => ({ cause })))) : string +>make : (s: T) => T +>yield* uuid.pipe(mapError((cause) => ({ cause }))) : string +>uuid.pipe(mapError((cause) => ({ cause }))) : Eff +>uuid.pipe : (f: (self: Eff) => B) => B +>uuid : Eff +>pipe : (f: (self: Eff) => B) => B +>mapError((cause) => ({ cause })) : (self: Eff) => Eff +>mapError : (f: (e: E) => E2) => (self: Eff) => Eff +>(cause) => ({ cause }) : (cause: Error) => { cause: Error; } +>cause : Error +>({ cause }) : { cause: Error; } +>{ cause } : { cause: Error; } +>cause : Error + +}); + diff --git a/tsc/testdata/baselines/reference/compiler/contextualCheckReentryAfterParameters.symbols b/tsc/testdata/baselines/reference/compiler/contextualCheckReentryAfterParameters.symbols new file mode 100644 index 0000000000000..695dd85162edd --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualCheckReentryAfterParameters.symbols @@ -0,0 +1,181 @@ +//// [tests/cases/compiler/contextualCheckReentryAfterParameters.ts] //// + +=== contextualCheckReentryAfterParameters.ts === +// Instantiating the type predicate inside the nested callback re-enters the +// contextual check of the enclosing functions through their outer type +// parameters. That must not discard the parameter types assigned to them. + +type Surface = { id: string; kind: "file"; path: string } | { id: string; kind: "terminal" }; +>Surface : Symbol(Surface, Decl(contextualCheckReentryAfterParameters.ts, 0, 0)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 4, 16)) +>kind : Symbol(kind, Decl(contextualCheckReentryAfterParameters.ts, 4, 28)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 4, 42)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 4, 61)) +>kind : Symbol(kind, Decl(contextualCheckReentryAfterParameters.ts, 4, 73)) + +interface Panel { +>Panel : Symbol(Panel, Decl(contextualCheckReentryAfterParameters.ts, 4, 93)) + + surfaces: Surface[]; +>surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>Surface : Symbol(Surface, Decl(contextualCheckReentryAfterParameters.ts, 0, 0)) +} +interface Store { +>Store : Symbol(Store, Decl(contextualCheckReentryAfterParameters.ts, 7, 1)) + + panel: Panel; +>panel : Symbol(Store.panel, Decl(contextualCheckReentryAfterParameters.ts, 8, 17)) +>Panel : Symbol(Panel, Decl(contextualCheckReentryAfterParameters.ts, 4, 93)) + + openFile: (path: string) => void; +>openFile : Symbol(Store.openFile, Decl(contextualCheckReentryAfterParameters.ts, 9, 17)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 10, 15)) + + close: (id: string) => void; +>close : Symbol(Store.close, Decl(contextualCheckReentryAfterParameters.ts, 10, 37)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 11, 12)) +} +type Creator = (set: (update: (state: T) => Partial) => void) => T; +>Creator : Symbol(Creator, Decl(contextualCheckReentryAfterParameters.ts, 12, 1)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 13, 13)) +>set : Symbol(set, Decl(contextualCheckReentryAfterParameters.ts, 13, 19)) +>update : Symbol(update, Decl(contextualCheckReentryAfterParameters.ts, 13, 25)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 13, 34)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 13, 13)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 13, 13)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 13, 13)) + +declare function create(init: Creator): T; +>create : Symbol(create, Decl(contextualCheckReentryAfterParameters.ts, 13, 73)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 14, 24)) +>init : Symbol(init, Decl(contextualCheckReentryAfterParameters.ts, 14, 27)) +>Creator : Symbol(Creator, Decl(contextualCheckReentryAfterParameters.ts, 12, 1)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 14, 24)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 14, 24)) + +declare function wrap(init: Creator): Creator; +>wrap : Symbol(wrap, Decl(contextualCheckReentryAfterParameters.ts, 14, 48)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 15, 22)) +>init : Symbol(init, Decl(contextualCheckReentryAfterParameters.ts, 15, 25)) +>Creator : Symbol(Creator, Decl(contextualCheckReentryAfterParameters.ts, 12, 1)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 15, 22)) +>Creator : Symbol(Creator, Decl(contextualCheckReentryAfterParameters.ts, 12, 1)) +>T : Symbol(T, Decl(contextualCheckReentryAfterParameters.ts, 15, 22)) + +const update = (state: Store, updater: (current: Panel) => Panel): Partial => ({ panel: updater(state.panel) }); +>update : Symbol(update, Decl(contextualCheckReentryAfterParameters.ts, 17, 5)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 17, 16)) +>Store : Symbol(Store, Decl(contextualCheckReentryAfterParameters.ts, 7, 1)) +>updater : Symbol(updater, Decl(contextualCheckReentryAfterParameters.ts, 17, 29)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 17, 40)) +>Panel : Symbol(Panel, Decl(contextualCheckReentryAfterParameters.ts, 4, 93)) +>Panel : Symbol(Panel, Decl(contextualCheckReentryAfterParameters.ts, 4, 93)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Store : Symbol(Store, Decl(contextualCheckReentryAfterParameters.ts, 7, 1)) +>panel : Symbol(panel, Decl(contextualCheckReentryAfterParameters.ts, 17, 87)) +>updater : Symbol(updater, Decl(contextualCheckReentryAfterParameters.ts, 17, 29)) +>state.panel : Symbol(Store.panel, Decl(contextualCheckReentryAfterParameters.ts, 8, 17)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 17, 16)) +>panel : Symbol(Store.panel, Decl(contextualCheckReentryAfterParameters.ts, 8, 17)) + +export const store = create(wrap((set) => ({ +>store : Symbol(store, Decl(contextualCheckReentryAfterParameters.ts, 19, 12)) +>create : Symbol(create, Decl(contextualCheckReentryAfterParameters.ts, 13, 73)) +>Store : Symbol(Store, Decl(contextualCheckReentryAfterParameters.ts, 7, 1)) +>wrap : Symbol(wrap, Decl(contextualCheckReentryAfterParameters.ts, 14, 48)) +>set : Symbol(set, Decl(contextualCheckReentryAfterParameters.ts, 19, 41)) + + panel: { surfaces: [] }, +>panel : Symbol(panel, Decl(contextualCheckReentryAfterParameters.ts, 19, 51)) +>surfaces : Symbol(surfaces, Decl(contextualCheckReentryAfterParameters.ts, 20, 12)) + + openFile: (path) => +>openFile : Symbol(openFile, Decl(contextualCheckReentryAfterParameters.ts, 20, 28)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 21, 15)) + + set((state) => +>set : Symbol(set, Decl(contextualCheckReentryAfterParameters.ts, 19, 41)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 22, 13)) + + update(state, (current) => { +>update : Symbol(update, Decl(contextualCheckReentryAfterParameters.ts, 17, 5)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 22, 13)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 23, 27)) + + const existing = current.surfaces.find( +>existing : Symbol(existing, Decl(contextualCheckReentryAfterParameters.ts, 24, 21)) +>current.surfaces.find : Symbol(Array.find, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>current.surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 23, 27)) +>surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>find : Symbol(Array.find, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) + + (surface): surface is Extract => surface.kind === "file" && surface.path === path, +>surface : Symbol(surface, Decl(contextualCheckReentryAfterParameters.ts, 25, 21)) +>surface : Symbol(surface, Decl(contextualCheckReentryAfterParameters.ts, 25, 21)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>Surface : Symbol(Surface, Decl(contextualCheckReentryAfterParameters.ts, 0, 0)) +>kind : Symbol(kind, Decl(contextualCheckReentryAfterParameters.ts, 25, 60)) +>surface.kind : Symbol(kind, Decl(contextualCheckReentryAfterParameters.ts, 4, 28), Decl(contextualCheckReentryAfterParameters.ts, 4, 73)) +>surface : Symbol(surface, Decl(contextualCheckReentryAfterParameters.ts, 25, 21)) +>kind : Symbol(kind, Decl(contextualCheckReentryAfterParameters.ts, 4, 28), Decl(contextualCheckReentryAfterParameters.ts, 4, 73)) +>surface.path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 4, 42)) +>surface : Symbol(surface, Decl(contextualCheckReentryAfterParameters.ts, 25, 21)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 4, 42)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 21, 15)) + + ); + return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; +>existing : Symbol(existing, Decl(contextualCheckReentryAfterParameters.ts, 24, 21)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 23, 27)) +>surfaces : Symbol(surfaces, Decl(contextualCheckReentryAfterParameters.ts, 27, 45)) +>current.surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 23, 27)) +>surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 27, 79)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 21, 15)) +>kind : Symbol(kind, Decl(contextualCheckReentryAfterParameters.ts, 27, 89)) +>path : Symbol(path, Decl(contextualCheckReentryAfterParameters.ts, 27, 103)) + + }) + ), + close: (id) => +>close : Symbol(close, Decl(contextualCheckReentryAfterParameters.ts, 29, 10)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 30, 12)) + + set((state) => +>set : Symbol(set, Decl(contextualCheckReentryAfterParameters.ts, 19, 41)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 31, 13)) + + update(state, (current) => { +>update : Symbol(update, Decl(contextualCheckReentryAfterParameters.ts, 17, 5)) +>state : Symbol(state, Decl(contextualCheckReentryAfterParameters.ts, 31, 13)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 32, 27)) + + const index = current.surfaces.findIndex((entry) => entry.id === id); +>index : Symbol(index, Decl(contextualCheckReentryAfterParameters.ts, 33, 21)) +>current.surfaces.findIndex : Symbol(Array.findIndex, Decl(lib.es2015.core.d.ts, --, --)) +>current.surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 32, 27)) +>surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>findIndex : Symbol(Array.findIndex, Decl(lib.es2015.core.d.ts, --, --)) +>entry : Symbol(entry, Decl(contextualCheckReentryAfterParameters.ts, 33, 58)) +>entry.id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 4, 16), Decl(contextualCheckReentryAfterParameters.ts, 4, 61)) +>entry : Symbol(entry, Decl(contextualCheckReentryAfterParameters.ts, 33, 58)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 4, 16), Decl(contextualCheckReentryAfterParameters.ts, 4, 61)) +>id : Symbol(id, Decl(contextualCheckReentryAfterParameters.ts, 30, 12)) + + return { surfaces: current.surfaces.slice(index) }; +>surfaces : Symbol(surfaces, Decl(contextualCheckReentryAfterParameters.ts, 34, 24)) +>current.surfaces.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>current.surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>current : Symbol(current, Decl(contextualCheckReentryAfterParameters.ts, 32, 27)) +>surfaces : Symbol(Panel.surfaces, Decl(contextualCheckReentryAfterParameters.ts, 5, 17)) +>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>index : Symbol(index, Decl(contextualCheckReentryAfterParameters.ts, 33, 21)) + + }) + ), +}))); + diff --git a/tsc/testdata/baselines/reference/compiler/contextualCheckReentryAfterParameters.types b/tsc/testdata/baselines/reference/compiler/contextualCheckReentryAfterParameters.types new file mode 100644 index 0000000000000..0fcb1000bcab9 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualCheckReentryAfterParameters.types @@ -0,0 +1,190 @@ +//// [tests/cases/compiler/contextualCheckReentryAfterParameters.ts] //// + +=== contextualCheckReentryAfterParameters.ts === +// Instantiating the type predicate inside the nested callback re-enters the +// contextual check of the enclosing functions through their outer type +// parameters. That must not discard the parameter types assigned to them. + +type Surface = { id: string; kind: "file"; path: string } | { id: string; kind: "terminal" }; +>Surface : Surface +>id : string +>kind : "file" +>path : string +>id : string +>kind : "terminal" + +interface Panel { + surfaces: Surface[]; +>surfaces : Surface[] +} +interface Store { + panel: Panel; +>panel : Panel + + openFile: (path: string) => void; +>openFile : (path: string) => void +>path : string + + close: (id: string) => void; +>close : (id: string) => void +>id : string +} +type Creator = (set: (update: (state: T) => Partial) => void) => T; +>Creator : Creator +>set : (update: (state: T) => Partial) => void +>update : (state: T) => Partial +>state : T + +declare function create(init: Creator): T; +>create : (init: Creator) => T +>init : Creator + +declare function wrap(init: Creator): Creator; +>wrap : (init: Creator) => Creator +>init : Creator + +const update = (state: Store, updater: (current: Panel) => Panel): Partial => ({ panel: updater(state.panel) }); +>update : (state: Store, updater: (current: Panel) => Panel) => Partial +>(state: Store, updater: (current: Panel) => Panel): Partial => ({ panel: updater(state.panel) }) : (state: Store, updater: (current: Panel) => Panel) => Partial +>state : Store +>updater : (current: Panel) => Panel +>current : Panel +>({ panel: updater(state.panel) }) : { panel: Panel; } +>{ panel: updater(state.panel) } : { panel: Panel; } +>panel : Panel +>updater(state.panel) : Panel +>updater : (current: Panel) => Panel +>state.panel : Panel +>state : Store +>panel : Panel + +export const store = create(wrap((set) => ({ +>store : Store +>create(wrap((set) => ({ panel: { surfaces: [] }, openFile: (path) => set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ), close: (id) => set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ),}))) : Store +>create : (init: Creator) => T +>wrap((set) => ({ panel: { surfaces: [] }, openFile: (path) => set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ), close: (id) => set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ),})) : Creator +>wrap : (init: Creator) => Creator +>(set) => ({ panel: { surfaces: [] }, openFile: (path) => set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ), close: (id) => set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ),}) : (set: (update: (state: Store) => Partial) => void) => { panel: { surfaces: never[]; }; openFile: (path: string) => void; close: (id: string) => void; } +>set : (update: (state: Store) => Partial) => void +>({ panel: { surfaces: [] }, openFile: (path) => set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ), close: (id) => set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ),}) : { panel: { surfaces: never[]; }; openFile: (path: string) => void; close: (id: string) => void; } +>{ panel: { surfaces: [] }, openFile: (path) => set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ), close: (id) => set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ),} : { panel: { surfaces: never[]; }; openFile: (path: string) => void; close: (id: string) => void; } + + panel: { surfaces: [] }, +>panel : { surfaces: never[]; } +>{ surfaces: [] } : { surfaces: never[]; } +>surfaces : never[] +>[] : never[] + + openFile: (path) => +>openFile : (path: string) => void +>(path) => set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ) : (path: string) => void +>path : string + + set((state) => +>set((state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) ) : void +>set : (update: (state: Store) => Partial) => void +>(state) => update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) : (state: Store) => Partial +>state : Store + + update(state, (current) => { +>update(state, (current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; }) : Partial +>update : (state: Store, updater: (current: Panel) => Panel) => Partial +>state : Store +>(current) => { const existing = current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ); return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; } : (current: Panel) => Panel +>current : Panel + + const existing = current.surfaces.find( +>existing : { id: string; kind: "file"; path: string; } | undefined +>current.surfaces.find( (surface): surface is Extract => surface.kind === "file" && surface.path === path, ) : { id: string; kind: "file"; path: string; } | undefined +>current.surfaces.find : { (predicate: (value: Surface, index: number, obj: Surface[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: Surface, index: number, obj: Surface[]) => unknown, thisArg?: any): Surface | undefined; } +>current.surfaces : Surface[] +>current : Panel +>surfaces : Surface[] +>find : { (predicate: (value: Surface, index: number, obj: Surface[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: Surface, index: number, obj: Surface[]) => unknown, thisArg?: any): Surface | undefined; } + + (surface): surface is Extract => surface.kind === "file" && surface.path === path, +>(surface): surface is Extract => surface.kind === "file" && surface.path === path : (surface: Surface) => surface is Extract +>surface : Surface +>kind : "file" +>surface.kind === "file" && surface.path === path : boolean +>surface.kind === "file" : boolean +>surface.kind : "file" | "terminal" +>surface : Surface +>kind : "file" | "terminal" +>"file" : "file" +>surface.path === path : boolean +>surface.path : string +>surface : { id: string; kind: "file"; path: string; } +>path : string +>path : string + + ); + return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; +>existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] } : Panel +>existing : { id: string; kind: "file"; path: string; } | undefined +>current : Panel +>{ surfaces: [...current.surfaces, { id: path, kind: "file", path }] } : { surfaces: Surface[]; } +>surfaces : Surface[] +>[...current.surfaces, { id: path, kind: "file", path }] : Surface[] +>...current.surfaces : Surface +>current.surfaces : Surface[] +>current : Panel +>surfaces : Surface[] +>{ id: path, kind: "file", path } : { id: string; kind: "file"; path: string; } +>id : string +>path : string +>kind : "file" +>"file" : "file" +>path : string + + }) + ), + close: (id) => +>close : (id: string) => void +>(id) => set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ) : (id: string) => void +>id : string + + set((state) => +>set((state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) ) : void +>set : (update: (state: Store) => Partial) => void +>(state) => update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) : (state: Store) => Partial +>state : Store + + update(state, (current) => { +>update(state, (current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; }) : Partial +>update : (state: Store, updater: (current: Panel) => Panel) => Partial +>state : Store +>(current) => { const index = current.surfaces.findIndex((entry) => entry.id === id); return { surfaces: current.surfaces.slice(index) }; } : (current: Panel) => { surfaces: Surface[]; } +>current : Panel + + const index = current.surfaces.findIndex((entry) => entry.id === id); +>index : number +>current.surfaces.findIndex((entry) => entry.id === id) : number +>current.surfaces.findIndex : (predicate: (value: Surface, index: number, obj: Surface[]) => unknown, thisArg?: any) => number +>current.surfaces : Surface[] +>current : Panel +>surfaces : Surface[] +>findIndex : (predicate: (value: Surface, index: number, obj: Surface[]) => unknown, thisArg?: any) => number +>(entry) => entry.id === id : (entry: Surface) => boolean +>entry : Surface +>entry.id === id : boolean +>entry.id : string +>entry : Surface +>id : string +>id : string + + return { surfaces: current.surfaces.slice(index) }; +>{ surfaces: current.surfaces.slice(index) } : { surfaces: Surface[]; } +>surfaces : Surface[] +>current.surfaces.slice(index) : Surface[] +>current.surfaces.slice : (start?: number, end?: number) => Surface[] +>current.surfaces : Surface[] +>current : Panel +>surfaces : Surface[] +>slice : (start?: number, end?: number) => Surface[] +>index : number + + }) + ), +}))); + diff --git a/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.js b/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.js new file mode 100644 index 0000000000000..ca1d3b91449da --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.js @@ -0,0 +1,36 @@ +//// [tests/cases/compiler/contextualReturnTypeInSpreadArgument.ts] //// + +//// [contextualReturnTypeInSpreadArgument.ts] +enum Kind { A, B } + +interface ItemA { kind: Kind.A; name: string } +interface ItemB { kind: Kind.B; size: number } +type Item = ItemA | ItemB; + +declare function create(options: { items?: () => readonly Item[]; label: string }): void; + +function test(flag: boolean) { + create({ + label: "x", + ...(flag ? {} : { + items: () => [{ kind: Kind.A, name: "a" }], + }), + }); +} + + +//// [contextualReturnTypeInSpreadArgument.js] +"use strict"; +var Kind; +(function (Kind) { + Kind[Kind["A"] = 0] = "A"; + Kind[Kind["B"] = 1] = "B"; +})(Kind || (Kind = {})); +function test(flag) { + create({ + label: "x", + ...(flag ? {} : { + items: () => [{ kind: Kind.A, name: "a" }], + }), + }); +} diff --git a/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.symbols b/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.symbols new file mode 100644 index 0000000000000..0802a21d1a19b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.symbols @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/contextualReturnTypeInSpreadArgument.ts] //// + +=== contextualReturnTypeInSpreadArgument.ts === +enum Kind { A, B } +>Kind : Symbol(Kind, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 0)) +>A : Symbol(Kind.A, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 11)) +>B : Symbol(Kind.B, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 14)) + +interface ItemA { kind: Kind.A; name: string } +>ItemA : Symbol(ItemA, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 18)) +>kind : Symbol(ItemA.kind, Decl(contextualReturnTypeInSpreadArgument.ts, 2, 17)) +>Kind : Symbol(Kind, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 0)) +>A : Symbol(Kind.A, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 11)) +>name : Symbol(ItemA.name, Decl(contextualReturnTypeInSpreadArgument.ts, 2, 31)) + +interface ItemB { kind: Kind.B; size: number } +>ItemB : Symbol(ItemB, Decl(contextualReturnTypeInSpreadArgument.ts, 2, 46)) +>kind : Symbol(ItemB.kind, Decl(contextualReturnTypeInSpreadArgument.ts, 3, 17)) +>Kind : Symbol(Kind, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 0)) +>B : Symbol(Kind.B, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 14)) +>size : Symbol(ItemB.size, Decl(contextualReturnTypeInSpreadArgument.ts, 3, 31)) + +type Item = ItemA | ItemB; +>Item : Symbol(Item, Decl(contextualReturnTypeInSpreadArgument.ts, 3, 46)) +>ItemA : Symbol(ItemA, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 18)) +>ItemB : Symbol(ItemB, Decl(contextualReturnTypeInSpreadArgument.ts, 2, 46)) + +declare function create(options: { items?: () => readonly Item[]; label: string }): void; +>create : Symbol(create, Decl(contextualReturnTypeInSpreadArgument.ts, 4, 26)) +>options : Symbol(options, Decl(contextualReturnTypeInSpreadArgument.ts, 6, 24)) +>items : Symbol(items, Decl(contextualReturnTypeInSpreadArgument.ts, 6, 34)) +>Item : Symbol(Item, Decl(contextualReturnTypeInSpreadArgument.ts, 3, 46)) +>label : Symbol(label, Decl(contextualReturnTypeInSpreadArgument.ts, 6, 65)) + +function test(flag: boolean) { +>test : Symbol(test, Decl(contextualReturnTypeInSpreadArgument.ts, 6, 89)) +>flag : Symbol(flag, Decl(contextualReturnTypeInSpreadArgument.ts, 8, 14)) + + create({ +>create : Symbol(create, Decl(contextualReturnTypeInSpreadArgument.ts, 4, 26)) + + label: "x", +>label : Symbol(label, Decl(contextualReturnTypeInSpreadArgument.ts, 9, 12)) + + ...(flag ? {} : { +>flag : Symbol(flag, Decl(contextualReturnTypeInSpreadArgument.ts, 8, 14)) + + items: () => [{ kind: Kind.A, name: "a" }], +>items : Symbol(items, Decl(contextualReturnTypeInSpreadArgument.ts, 11, 25)) +>kind : Symbol(kind, Decl(contextualReturnTypeInSpreadArgument.ts, 12, 27)) +>Kind.A : Symbol(Kind.A, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 11)) +>Kind : Symbol(Kind, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 0)) +>A : Symbol(Kind.A, Decl(contextualReturnTypeInSpreadArgument.ts, 0, 11)) +>name : Symbol(name, Decl(contextualReturnTypeInSpreadArgument.ts, 12, 41)) + + }), + }); +} + diff --git a/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.types b/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.types new file mode 100644 index 0000000000000..6c1780590bad8 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/contextualReturnTypeInSpreadArgument.types @@ -0,0 +1,63 @@ +//// [tests/cases/compiler/contextualReturnTypeInSpreadArgument.ts] //// + +=== contextualReturnTypeInSpreadArgument.ts === +enum Kind { A, B } +>Kind : Kind +>A : Kind.A +>B : Kind.B + +interface ItemA { kind: Kind.A; name: string } +>kind : Kind.A +>Kind : any +>name : string + +interface ItemB { kind: Kind.B; size: number } +>kind : Kind.B +>Kind : any +>size : number + +type Item = ItemA | ItemB; +>Item : Item + +declare function create(options: { items?: () => readonly Item[]; label: string }): void; +>create : (options: { items?: () => readonly Item[]; label: string; }) => void +>options : { items?: () => readonly Item[]; label: string; } +>items : (() => readonly Item[]) | undefined +>label : string + +function test(flag: boolean) { +>test : (flag: boolean) => void +>flag : boolean + + create({ +>create({ label: "x", ...(flag ? {} : { items: () => [{ kind: Kind.A, name: "a" }], }), }) : void +>create : (options: { items?: () => readonly Item[]; label: string; }) => void +>{ label: "x", ...(flag ? {} : { items: () => [{ kind: Kind.A, name: "a" }], }), } : { label: string; items?: (() => { kind: Kind.A; name: string; }[]) | undefined; } + + label: "x", +>label : string +>"x" : "x" + + ...(flag ? {} : { +>(flag ? {} : { items: () => [{ kind: Kind.A, name: "a" }], }) : {} | { items: () => { kind: Kind.A; name: string; }[]; } +>flag ? {} : { items: () => [{ kind: Kind.A, name: "a" }], } : {} | { items: () => { kind: Kind.A; name: string; }[]; } +>flag : boolean +>{} : {} +>{ items: () => [{ kind: Kind.A, name: "a" }], } : { items: () => { kind: Kind.A; name: string; }[]; } + + items: () => [{ kind: Kind.A, name: "a" }], +>items : () => { kind: Kind.A; name: string; }[] +>() => [{ kind: Kind.A, name: "a" }] : () => { kind: Kind.A; name: string; }[] +>[{ kind: Kind.A, name: "a" }] : { kind: Kind.A; name: string; }[] +>{ kind: Kind.A, name: "a" } : { kind: Kind.A; name: string; } +>kind : Kind.A +>Kind.A : Kind.A +>Kind : typeof Kind +>A : Kind.A +>name : string +>"a" : "a" + + }), + }); +} + diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js index 935e9baeed1ad..c43a610ffef9d 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.js @@ -77,5 +77,7 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types index f7cb243d2a23e..ed5fe02f28302 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitExternalModuleContainerIndexDeclarationOrder.types @@ -26,8 +26,8 @@ export const obsA = expand(); >expand : () => { [K in keyof X]: X[K]; } export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } === /ambient.d.ts === diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport(target=es2015).errors.txt b/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport(target=es2015).errors.txt index 512f22fa8a9b6..8dcbaf204205d 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport(target=es2015).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport(target=es2015).errors.txt @@ -1,11 +1,8 @@ -declarationEmitUnknownImport.ts(1,14): error TS2304: Cannot find name 'SomeNonExistingName'. declarationEmitUnknownImport.ts(1,14): error TS2503: Cannot find namespace 'SomeNonExistingName'. -==== declarationEmitUnknownImport.ts (2 errors) ==== +==== declarationEmitUnknownImport.ts (1 errors) ==== import Foo = SomeNonExistingName ~~~~~~~~~~~~~~~~~~~ -!!! error TS2304: Cannot find name 'SomeNonExistingName'. - ~~~~~~~~~~~~~~~~~~~ !!! error TS2503: Cannot find namespace 'SomeNonExistingName'. export {Foo} \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport2(target=es2015).errors.txt b/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport2(target=es2015).errors.txt index 07239f7d2748f..db5cb25071de4 100644 --- a/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport2(target=es2015).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitUnknownImport2(target=es2015).errors.txt @@ -1,16 +1,13 @@ declarationEmitUnknownImport2.ts(1,12): error TS1005: '=' expected. -declarationEmitUnknownImport2.ts(1,12): error TS2304: Cannot find name 'From'. declarationEmitUnknownImport2.ts(1,12): error TS2503: Cannot find namespace 'From'. declarationEmitUnknownImport2.ts(1,17): error TS1005: ';' expected. -==== declarationEmitUnknownImport2.ts (4 errors) ==== +==== declarationEmitUnknownImport2.ts (3 errors) ==== import Foo From './Foo'; // Syntax error ~~~~ !!! error TS1005: '=' expected. ~~~~ -!!! error TS2304: Cannot find name 'From'. - ~~~~ !!! error TS2503: Cannot find namespace 'From'. ~~~~~~~ !!! error TS1005: ';' expected. diff --git a/tsc/testdata/baselines/reference/compiler/dependentDestructuringBindingElementInitializer(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/dependentDestructuringBindingElementInitializer(checkorder=typesfirst).types index 265e9b075cacf..0dcd3d8a64cef 100644 --- a/tsc/testdata/baselines/reference/compiler/dependentDestructuringBindingElementInitializer(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/dependentDestructuringBindingElementInitializer(checkorder=typesfirst).types @@ -25,8 +25,8 @@ const { >payload : string | number extra = kind === "a" ? payload.toFixed() : payload.toUpperCase(), ->extra : any ->kind === "a" ? payload.toFixed() : payload.toUpperCase() : any +>extra : unknown +>kind === "a" ? payload.toFixed() : payload.toUpperCase() : string >kind === "a" : boolean >kind : "a" | "b" >"a" : "a" diff --git a/tsc/testdata/baselines/reference/compiler/excessivelyLargeTupleSpread(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/excessivelyLargeTupleSpread(checkorder=typesfirst).errors.txt index 5bb90d57b8a95..6ec6fc6629d8a 100644 --- a/tsc/testdata/baselines/reference/compiler/excessivelyLargeTupleSpread(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/excessivelyLargeTupleSpread(checkorder=typesfirst).errors.txt @@ -1,15 +1,17 @@ -error TS2800: Expression produces a tuple type that is too large to represent. +excessivelyLargeTupleSpread.ts(6,10): error TS2799: Type produces a tuple type that is too large to represent. +excessivelyLargeTupleSpread.ts(22,12): error TS2799: Type produces a tuple type that is too large to represent. excessivelyLargeTupleSpread.ts(38,13): error TS2800: Expression produces a tuple type that is too large to represent. -!!! error TS2800: Expression produces a tuple type that is too large to represent. -==== excessivelyLargeTupleSpread.ts (1 errors) ==== +==== excessivelyLargeTupleSpread.ts (3 errors) ==== // #41771 type BuildTuple = T['length'] extends L ? T : BuildTuple; type A = BuildTuple<3> + ~~~~~~~~~~~~~ +!!! error TS2799: Type produces a tuple type that is too large to represent. type T0 = [any]; type T1 = [...T0, ...T0]; @@ -26,6 +28,8 @@ excessivelyLargeTupleSpread.ts(38,13): error TS2800: Expression produces a tuple type T12 = [...T11, ...T11]; type T13 = [...T12, ...T12]; type T14 = [...T13, ...T13]; // 2^14 > 10,000 + ~~~~~~~~~~~~~~~~ +!!! error TS2799: Type produces a tuple type that is too large to represent. const a0 = [0] as const; const a1 = [...a0, ...a0] as const; diff --git a/tsc/testdata/baselines/reference/compiler/importDeclWithClassModifiers.errors.txt b/tsc/testdata/baselines/reference/compiler/importDeclWithClassModifiers.errors.txt index 12e30795fff58..ddfbafc65341d 100644 --- a/tsc/testdata/baselines/reference/compiler/importDeclWithClassModifiers.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/importDeclWithClassModifiers.errors.txt @@ -1,15 +1,12 @@ importDeclWithClassModifiers.ts(5,8): error TS1044: 'public' modifier cannot appear on a module or namespace element. -importDeclWithClassModifiers.ts(5,26): error TS2708: Cannot use namespace 'x' as a value. importDeclWithClassModifiers.ts(5,28): error TS2694: Namespace 'x' has no exported member 'c'. importDeclWithClassModifiers.ts(6,8): error TS1044: 'private' modifier cannot appear on a module or namespace element. -importDeclWithClassModifiers.ts(6,27): error TS2708: Cannot use namespace 'x' as a value. importDeclWithClassModifiers.ts(6,29): error TS2694: Namespace 'x' has no exported member 'c'. importDeclWithClassModifiers.ts(7,8): error TS1044: 'static' modifier cannot appear on a module or namespace element. -importDeclWithClassModifiers.ts(7,26): error TS2708: Cannot use namespace 'x' as a value. importDeclWithClassModifiers.ts(7,28): error TS2694: Namespace 'x' has no exported member 'c'. -==== importDeclWithClassModifiers.ts (9 errors) ==== +==== importDeclWithClassModifiers.ts (6 errors) ==== namespace x { interface c { } @@ -17,22 +14,16 @@ importDeclWithClassModifiers.ts(7,28): error TS2694: Namespace 'x' has no export export public import a = x.c; ~~~~~~ !!! error TS1044: 'public' modifier cannot appear on a module or namespace element. - ~ -!!! error TS2708: Cannot use namespace 'x' as a value. ~ !!! error TS2694: Namespace 'x' has no exported member 'c'. export private import b = x.c; ~~~~~~~ !!! error TS1044: 'private' modifier cannot appear on a module or namespace element. - ~ -!!! error TS2708: Cannot use namespace 'x' as a value. ~ !!! error TS2694: Namespace 'x' has no exported member 'c'. export static import c = x.c; ~~~~~~ !!! error TS1044: 'static' modifier cannot appear on a module or namespace element. - ~ -!!! error TS2708: Cannot use namespace 'x' as a value. ~ !!! error TS2694: Namespace 'x' has no exported member 'c'. var b: a; diff --git a/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifier.errors.txt b/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifier.errors.txt index 8da059494431f..282fc28e8acd5 100644 --- a/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifier.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifier.errors.txt @@ -1,15 +1,12 @@ -importDeclWithExportModifier.ts(5,19): error TS2708: Cannot use namespace 'x' as a value. importDeclWithExportModifier.ts(5,21): error TS2694: Namespace 'x' has no exported member 'c'. -==== importDeclWithExportModifier.ts (2 errors) ==== +==== importDeclWithExportModifier.ts (1 errors) ==== namespace x { interface c { } } export import a = x.c; - ~ -!!! error TS2708: Cannot use namespace 'x' as a value. ~ !!! error TS2694: Namespace 'x' has no exported member 'c'. var b: a; diff --git a/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifierAndExportAssignment.errors.txt b/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifierAndExportAssignment.errors.txt index d4420d401f29a..083e112ea0c32 100644 --- a/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifierAndExportAssignment.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/importDeclWithExportModifierAndExportAssignment.errors.txt @@ -1,16 +1,13 @@ -importDeclWithExportModifierAndExportAssignment.ts(5,19): error TS2708: Cannot use namespace 'x' as a value. importDeclWithExportModifierAndExportAssignment.ts(5,21): error TS2694: Namespace 'x' has no exported member 'c'. importDeclWithExportModifierAndExportAssignment.ts(6,1): error TS2309: An export assignment cannot be used in a module with other exported elements. -==== importDeclWithExportModifierAndExportAssignment.ts (3 errors) ==== +==== importDeclWithExportModifierAndExportAssignment.ts (2 errors) ==== namespace x { interface c { } } export import a = x.c; - ~ -!!! error TS2708: Cannot use namespace 'x' as a value. ~ !!! error TS2694: Namespace 'x' has no exported member 'c'. export = x; diff --git a/tsc/testdata/baselines/reference/compiler/incorrectRecursiveMappedTypeConstraint.errors.txt b/tsc/testdata/baselines/reference/compiler/incorrectRecursiveMappedTypeConstraint.errors.txt index 41f682e455f8d..4fc215b0dd52d 100644 --- a/tsc/testdata/baselines/reference/compiler/incorrectRecursiveMappedTypeConstraint.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/incorrectRecursiveMappedTypeConstraint.errors.txt @@ -8,10 +8,8 @@ incorrectRecursiveMappedTypeConstraint.ts(3,5): error TS2365: Operator '+=' cann function sum(n: number, v: T, k: K) { ~~~~~~~~~~~~~~~~~~~~ !!! error TS2313: Type parameter 'T' has a circular constraint. -!!! related TS2751 incorrectRecursiveMappedTypeConstraint.ts:3:10: Circularity originates in type at this location. ~ !!! error TS2313: Type parameter 'P' has a circular constraint. -!!! related TS2751 incorrectRecursiveMappedTypeConstraint.ts:3:10: Circularity originates in type at this location. n += v[k]; ~~~~~~~~~ !!! error TS2365: Operator '+=' cannot be applied to types 'number' and 'T[K]'. diff --git a/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=false,verbatimmodulesyntax=false).errors.txt b/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=false,verbatimmodulesyntax=false).errors.txt index 4687c6ca6a727..8f54167c9ef56 100644 --- a/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=false,verbatimmodulesyntax=false).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=false,verbatimmodulesyntax=false).errors.txt @@ -1,14 +1,11 @@ -a.ts(2,23): error TS2304: Cannot find name 'x'. a.ts(2,23): error TS2503: Cannot find namespace 'x'. a.ts(13,5): error TS2440: Import declaration conflicts with local declaration of 'A'. -==== a.ts (3 errors) ==== +==== a.ts (2 errors) ==== namespace a { export import A = x.A; ~ -!!! error TS2304: Cannot find name 'x'. - ~ !!! error TS2503: Cannot find namespace 'x'. export type A = number; } diff --git a/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt b/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt index adf7becde9bb2..e4cc587d6a4c5 100644 --- a/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/isolatedModulesMergedImportAlias(isolatedmodules=true,verbatimmodulesyntax=false).errors.txt @@ -1,18 +1,15 @@ a.ts(1,11): error TS1280: Namespaces are not allowed in global script files when 'isolatedModules' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement. -a.ts(2,23): error TS2304: Cannot find name 'x'. a.ts(2,23): error TS2503: Cannot find namespace 'x'. a.ts(12,11): error TS1280: Namespaces are not allowed in global script files when 'isolatedModules' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement. a.ts(13,5): error TS2440: Import declaration conflicts with local declaration of 'A'. -==== a.ts (5 errors) ==== +==== a.ts (4 errors) ==== namespace a { ~ !!! error TS1280: Namespaces are not allowed in global script files when 'isolatedModules' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement. export import A = x.A; ~ -!!! error TS2304: Cannot find name 'x'. - ~ !!! error TS2503: Cannot find namespace 'x'. export type A = number; } diff --git a/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.errors.txt b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.errors.txt new file mode 100644 index 0000000000000..bafe2d1546822 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.errors.txt @@ -0,0 +1,19 @@ +file.js(4,11): error TS7022: 'pixel' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +file.js(4,19): error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ skip: any; }'. +file.js(5,24): error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ skip: any; }'. + + +==== file.js (3 errors) ==== + /** @param {any} axis @param {any} value */ + function updateElements(axis, value) { + const properties = {}; + const pixel = properties[axis] = value; + ~~~~~ +!!! error TS7022: 'pixel' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + ~~~~~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ skip: any; }'. + const otherPixel = properties[axis] = value; + ~~~~~~~~~~~~~~~~ +!!! error TS7053: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ skip: any; }'. + properties.skip = isNaN(pixel) || isNaN(otherPixel); + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.js b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.js new file mode 100644 index 0000000000000..d660e1f2146fc --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.js @@ -0,0 +1,20 @@ +//// [tests/cases/compiler/jsExpandoIndexedAssignmentDiagnostic.ts] //// + +//// [file.js] +/** @param {any} axis @param {any} value */ +function updateElements(axis, value) { + const properties = {}; + const pixel = properties[axis] = value; + const otherPixel = properties[axis] = value; + properties.skip = isNaN(pixel) || isNaN(otherPixel); +} + +//// [file.js] +"use strict"; +/** @param {any} axis @param {any} value */ +function updateElements(axis, value) { + const properties = {}; + const pixel = properties[axis] = value; + const otherPixel = properties[axis] = value; + properties.skip = isNaN(pixel) || isNaN(otherPixel); +} diff --git a/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.symbols b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.symbols new file mode 100644 index 0000000000000..373963a684ab3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.symbols @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/jsExpandoIndexedAssignmentDiagnostic.ts] //// + +=== file.js === +/** @param {any} axis @param {any} value */ +function updateElements(axis, value) { +>updateElements : Symbol(updateElements, Decl(file.js, 0, 0)) +>axis : Symbol(axis, Decl(file.js, 1, 24)) +>value : Symbol(value, Decl(file.js, 1, 29)) + + const properties = {}; +>properties : Symbol(properties, Decl(file.js, 2, 9)) + + const pixel = properties[axis] = value; +>pixel : Symbol(pixel, Decl(file.js, 3, 9)) +>properties : Symbol(properties, Decl(file.js, 2, 9)) +>axis : Symbol(axis, Decl(file.js, 1, 24)) +>value : Symbol(value, Decl(file.js, 1, 29)) + + const otherPixel = properties[axis] = value; +>otherPixel : Symbol(otherPixel, Decl(file.js, 4, 9)) +>properties : Symbol(properties, Decl(file.js, 2, 9)) +>axis : Symbol(axis, Decl(file.js, 1, 24)) +>value : Symbol(value, Decl(file.js, 1, 29)) + + properties.skip = isNaN(pixel) || isNaN(otherPixel); +>properties.skip : Symbol(skip, Decl(file.js, 4, 48)) +>properties : Symbol(properties, Decl(file.js, 2, 9)) +>skip : Symbol(skip, Decl(file.js, 4, 48)) +>isNaN : Symbol(isNaN, Decl(lib.es5.d.ts, --, --)) +>pixel : Symbol(pixel, Decl(file.js, 3, 9)) +>isNaN : Symbol(isNaN, Decl(lib.es5.d.ts, --, --)) +>otherPixel : Symbol(otherPixel, Decl(file.js, 4, 9)) +} diff --git a/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.types b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.types new file mode 100644 index 0000000000000..607a8ed056027 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/jsExpandoIndexedAssignmentDiagnostic.types @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/jsExpandoIndexedAssignmentDiagnostic.ts] //// + +=== file.js === +/** @param {any} axis @param {any} value */ +function updateElements(axis, value) { +>updateElements : (axis: any, value: any) => void +>axis : any +>value : any + + const properties = {}; +>properties : { skip: any; } +>{} : { skip: any; } + + const pixel = properties[axis] = value; +>pixel : any +>properties[axis] = value : any +>properties[axis] : any +>properties : { skip: any; } +>axis : any +>value : any + + const otherPixel = properties[axis] = value; +>otherPixel : any +>properties[axis] = value : any +>properties[axis] : any +>properties : { skip: any; } +>axis : any +>value : any + + properties.skip = isNaN(pixel) || isNaN(otherPixel); +>properties.skip = isNaN(pixel) || isNaN(otherPixel) : boolean +>properties.skip : any +>properties : { skip: any; } +>skip : any +>isNaN(pixel) || isNaN(otherPixel) : boolean +>isNaN(pixel) : boolean +>isNaN : (number: number) => boolean +>pixel : any +>isNaN(otherPixel) : boolean +>isNaN : (number: number) => boolean +>otherPixel : any +} diff --git a/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.errors.txt b/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.errors.txt index f19e5b3afede1..f9f9a2d8d5885 100644 --- a/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.errors.txt @@ -2,17 +2,13 @@ a.js(4,1): error TS2322: Type '(content: string, rest_0?: boolean | undefined) = Types of parameters 'content' and 'a' are incompatible. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'. -a.js(6,35): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'. - Property 'toUpperCase' does not exist on type 'number'. a.js(9,7): error TS2322: Type '(content: string) => string' is not assignable to type '(content: string | number) => string'. Types of parameters 'content' and 'content' are incompatible. Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'. -a.js(11,26): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'. - Property 'toUpperCase' does not exist on type 'number'. -==== a.js (4 errors) ==== +==== a.js (2 errors) ==== /** @type {{ f: (a: string | number, ...rest: unknown[]) => string }} */ const context = { f: (a) => "" }; @@ -24,9 +20,6 @@ a.js(11,26): error TS2339: Property 'toUpperCase' does not exist on type 'string !!! error TS2322: Type 'number' is not assignable to type 'string'. /** @type {(content: string, ...rest: [boolean?]) => string} */ (content, ...rest) => content.toUpperCase() + rest.length; - ~~~~~~~~~~~ -!!! error TS2339: Property 'toUpperCase' does not exist on type 'string | number'. -!!! error TS2339: Property 'toUpperCase' does not exist on type 'number'. /** @type {(content: string | number) => string} */ const g = @@ -37,7 +30,4 @@ a.js(11,26): error TS2339: Property 'toUpperCase' does not exist on type 'string !!! error TS2322: Type 'number' is not assignable to type 'string'. /** @type {(content: string) => string} */ (content) => content.toUpperCase(); - ~~~~~~~~~~~ -!!! error TS2339: Property 'toUpperCase' does not exist on type 'string | number'. -!!! error TS2339: Property 'toUpperCase' does not exist on type 'number'. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.symbols b/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.symbols index 17ba749362f25..15ffe15bb65e2 100644 --- a/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.symbols +++ b/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.symbols @@ -16,10 +16,12 @@ context.f = (content, ...rest) => content.toUpperCase() + rest.length; >content : Symbol(content, Decl(a.js, 5, 5)) >rest : Symbol(rest, Decl(a.js, 5, 13)) +>content.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) >content : Symbol(content, Decl(a.js, 5, 5)) ->rest.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>rest.length : Symbol(length) >rest : Symbol(rest, Decl(a.js, 5, 13)) ->length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>length : Symbol(length) /** @type {(content: string | number) => string} */ const g = @@ -28,5 +30,7 @@ const g = /** @type {(content: string) => string} */ (content) => content.toUpperCase(); >content : Symbol(content, Decl(a.js, 10, 5)) +>content.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) >content : Symbol(content, Decl(a.js, 10, 5)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) diff --git a/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.types b/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.types index 7511697864aa1..6defe743de587 100644 --- a/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.types +++ b/tsc/testdata/baselines/reference/compiler/jsdocTypeTagOnContextuallyTypedFunction.types @@ -19,16 +19,16 @@ context.f = /** @type {(content: string, ...rest: [boolean?]) => string} */ (content, ...rest) => content.toUpperCase() + rest.length; >(content, ...rest) => content.toUpperCase() + rest.length : (content: string, rest_0?: boolean | undefined) => string ->content : string | number ->rest : unknown[] ->content.toUpperCase() + rest.length : any ->content.toUpperCase() : any ->content.toUpperCase : any ->content : string | number ->toUpperCase : any ->rest.length : number ->rest : unknown[] ->length : number +>content : string +>rest : [(boolean | undefined)?] +>content.toUpperCase() + rest.length : string +>content.toUpperCase() : string +>content.toUpperCase : () => string +>content : string +>toUpperCase : () => string +>rest.length : 0 | 1 +>rest : [(boolean | undefined)?] +>length : 0 | 1 /** @type {(content: string | number) => string} */ const g = @@ -37,9 +37,9 @@ const g = /** @type {(content: string) => string} */ (content) => content.toUpperCase(); >(content) => content.toUpperCase() : (content: string) => string ->content : string | number ->content.toUpperCase() : any ->content.toUpperCase : any ->content : string | number ->toUpperCase : any +>content : string +>content.toUpperCase() : string +>content.toUpperCase : () => string +>content : string +>toUpperCase : () => string diff --git a/tsc/testdata/baselines/reference/compiler/jsxImportSourceNonPragmaComment(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/jsxImportSourceNonPragmaComment(checkorder=typesfirst).errors.txt index b45ab387a625b..191dc50e4166c 100644 --- a/tsc/testdata/baselines/reference/compiler/jsxImportSourceNonPragmaComment(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/jsxImportSourceNonPragmaComment(checkorder=typesfirst).errors.txt @@ -1,10 +1,9 @@ jsxImportSourceNonPragmaComment.tsx(3,21): error TS2307: Cannot find module '@emotion/react' or its corresponding type declarations. jsxImportSourceNonPragmaComment.tsx(7,5): error TS2875: This JSX tag requires the module path '@emotion/react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. jsxImportSourceNonPragmaComment.tsx(7,5): error TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists. -jsxImportSourceNonPragmaComment.tsx(7,6): error TS2552: Cannot find name 'input'. Did you mean 'oninput'? -==== jsxImportSourceNonPragmaComment.tsx (4 errors) ==== +==== jsxImportSourceNonPragmaComment.tsx (3 errors) ==== /* eslint-disable react/react-in-jsx-scope -- Unaware of @jsxImportSource */ /** @jsxImportSource @emotion/react */ import { css } from "@emotion/react"; @@ -16,9 +15,6 @@ jsxImportSourceNonPragmaComment.tsx(7,6): error TS2552: Cannot find name 'input' {} - } - - ; - ~ -!!! error TS2304: Cannot find name 'a'. - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/jsxIntrinsicElementsExtendsRecord(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/jsxIntrinsicElementsExtendsRecord(checkorder=typesfirst).types index a2a76b6e77eed..30632f23d7dc4 100644 --- a/tsc/testdata/baselines/reference/compiler/jsxIntrinsicElementsExtendsRecord(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/jsxIntrinsicElementsExtendsRecord(checkorder=typesfirst).types @@ -6,6 +6,6 @@ declare namespace JSX { } ; -> : any +> : error >a : any diff --git a/tsc/testdata/baselines/reference/compiler/keyofGenericExtendingClassDoubleLayer.errors.txt b/tsc/testdata/baselines/reference/compiler/keyofGenericExtendingClassDoubleLayer.errors.txt index 9808b4f1f9122..8c8a4f0003e86 100644 --- a/tsc/testdata/baselines/reference/compiler/keyofGenericExtendingClassDoubleLayer.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/keyofGenericExtendingClassDoubleLayer.errors.txt @@ -1,9 +1,10 @@ keyofGenericExtendingClassDoubleLayer.ts(2,12): error TS2564: Property 'createdAt' has no initializer and is not definitely assigned in the constructor. -keyofGenericExtendingClassDoubleLayer.ts(9,7): error TS5115: Instantiations of the following types appear infinitely circular: 'Model', 'ModelAttributes', 'Exclude'. +keyofGenericExtendingClassDoubleLayer.ts(9,7): error TS5115: Instantiations of the following types appear infinitely circular: 'Exclude', 'Model', 'ModelAttributes'. keyofGenericExtendingClassDoubleLayer.ts(10,12): error TS2564: Property 'age' has no initializer and is not definitely assigned in the constructor. +keyofGenericExtendingClassDoubleLayer.ts(13,13): error TS5115: Instantiations of the following types appear infinitely circular: 'Exclude', 'Model', 'ModelAttributes'. -==== keyofGenericExtendingClassDoubleLayer.ts (3 errors) ==== +==== keyofGenericExtendingClassDoubleLayer.ts (4 errors) ==== class Model { public createdAt: Date; ~~~~~~~~~ @@ -16,13 +17,15 @@ keyofGenericExtendingClassDoubleLayer.ts(10,12): error TS2564: Property 'age' ha class PersonModel extends AutoModel { ~~~~~~~~~~~ -!!! error TS5115: Instantiations of the following types appear infinitely circular: 'Model', 'ModelAttributes', 'Exclude'. +!!! error TS5115: Instantiations of the following types appear infinitely circular: 'Exclude', 'Model', 'ModelAttributes'. public age: number; ~~~ !!! error TS2564: Property 'age' has no initializer and is not definitely assigned in the constructor. toJson() { let x: keyof this = 'createdAt'; + ~ +!!! error TS5115: Instantiations of the following types appear infinitely circular: 'Exclude', 'Model', 'ModelAttributes'. } } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.errors.txt b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.errors.txt new file mode 100644 index 0000000000000..c1b01f83c3c86 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.errors.txt @@ -0,0 +1,26 @@ +lateBoundMutualMemberTables.ts(6,8): error TS2339: Property 'by' does not exist on type 'B<"b">'. +lateBoundMutualMemberTables.ts(15,21): error TS2339: Property 'fromA2' does not exist on type 'A<"a">'. + + +==== lateBoundMutualMemberTables.ts (2 errors) ==== + declare const a: A<"a">; + declare const b: B<"b">; + declare class A { + x: T; + [b.y]: "fromB"; + [b.by]: "fromB2"; + ~~ +!!! error TS2339: Property 'by' does not exist on type 'B<"b">'. + } + declare class B { + y: T; + [a.x]: "fromA"; + [a.b]: "fromA2"; + } + export const v1 = a.b; + export const v2 = b.a; + export const v3 = a.fromA2; + ~~~~~~ +!!! error TS2339: Property 'fromA2' does not exist on type 'A<"a">'. + export const v4 = b.fromB; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.js b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.js new file mode 100644 index 0000000000000..aea4e1ea51859 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.js @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/lateBoundMutualMemberTables.ts] //// + +//// [lateBoundMutualMemberTables.ts] +declare const a: A<"a">; +declare const b: B<"b">; +declare class A { + x: T; + [b.y]: "fromB"; + [b.by]: "fromB2"; +} +declare class B { + y: T; + [a.x]: "fromA"; + [a.b]: "fromA2"; +} +export const v1 = a.b; +export const v2 = b.a; +export const v3 = a.fromA2; +export const v4 = b.fromB; + + +//// [lateBoundMutualMemberTables.js] +export const v1 = a.b; +export const v2 = b.a; +export const v3 = a.fromA2; +export const v4 = b.fromB; + + +//// [lateBoundMutualMemberTables.d.ts] +export declare const v1: "fromB"; +export declare const v2: "fromA"; +export declare const v3: any; +export declare const v4: "fromA2"; diff --git a/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.symbols b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.symbols new file mode 100644 index 0000000000000..24eba3a29f260 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.symbols @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/lateBoundMutualMemberTables.ts] //// + +=== lateBoundMutualMemberTables.ts === +declare const a: A<"a">; +>a : Symbol(a, Decl(lateBoundMutualMemberTables.ts, 0, 13)) +>A : Symbol(A, Decl(lateBoundMutualMemberTables.ts, 1, 24)) + +declare const b: B<"b">; +>b : Symbol(b, Decl(lateBoundMutualMemberTables.ts, 1, 13)) +>B : Symbol(B, Decl(lateBoundMutualMemberTables.ts, 6, 1)) + +declare class A { +>A : Symbol(A, Decl(lateBoundMutualMemberTables.ts, 1, 24)) +>T : Symbol(T, Decl(lateBoundMutualMemberTables.ts, 2, 16)) + + x: T; +>x : Symbol(A.x, Decl(lateBoundMutualMemberTables.ts, 2, 20)) +>T : Symbol(T, Decl(lateBoundMutualMemberTables.ts, 2, 16)) + + [b.y]: "fromB"; +>[b.y] : Symbol(A[b.y], Decl(lateBoundMutualMemberTables.ts, 3, 9)) +>b.y : Symbol(B.y, Decl(lateBoundMutualMemberTables.ts, 7, 20)) +>b : Symbol(b, Decl(lateBoundMutualMemberTables.ts, 1, 13)) +>y : Symbol(B.y, Decl(lateBoundMutualMemberTables.ts, 7, 20)) + + [b.by]: "fromB2"; +>[b.by] : Symbol(A[b.by], Decl(lateBoundMutualMemberTables.ts, 4, 19)) +>b : Symbol(b, Decl(lateBoundMutualMemberTables.ts, 1, 13)) +} +declare class B { +>B : Symbol(B, Decl(lateBoundMutualMemberTables.ts, 6, 1)) +>T : Symbol(T, Decl(lateBoundMutualMemberTables.ts, 7, 16)) + + y: T; +>y : Symbol(B.y, Decl(lateBoundMutualMemberTables.ts, 7, 20)) +>T : Symbol(T, Decl(lateBoundMutualMemberTables.ts, 7, 16)) + + [a.x]: "fromA"; +>[a.x] : Symbol(B[a.x], Decl(lateBoundMutualMemberTables.ts, 8, 9)) +>a.x : Symbol(A.x, Decl(lateBoundMutualMemberTables.ts, 2, 20)) +>a : Symbol(a, Decl(lateBoundMutualMemberTables.ts, 0, 13)) +>x : Symbol(A.x, Decl(lateBoundMutualMemberTables.ts, 2, 20)) + + [a.b]: "fromA2"; +>[a.b] : Symbol(B[a.b], Decl(lateBoundMutualMemberTables.ts, 9, 19)) +>a.b : Symbol(A[b.y], Decl(lateBoundMutualMemberTables.ts, 3, 9)) +>a : Symbol(a, Decl(lateBoundMutualMemberTables.ts, 0, 13)) +>b : Symbol(A[b.y], Decl(lateBoundMutualMemberTables.ts, 3, 9)) +} +export const v1 = a.b; +>v1 : Symbol(v1, Decl(lateBoundMutualMemberTables.ts, 12, 12)) +>a.b : Symbol(A[b.y], Decl(lateBoundMutualMemberTables.ts, 3, 9)) +>a : Symbol(a, Decl(lateBoundMutualMemberTables.ts, 0, 13)) +>b : Symbol(A[b.y], Decl(lateBoundMutualMemberTables.ts, 3, 9)) + +export const v2 = b.a; +>v2 : Symbol(v2, Decl(lateBoundMutualMemberTables.ts, 13, 12)) +>b.a : Symbol(B[a.x], Decl(lateBoundMutualMemberTables.ts, 8, 9)) +>b : Symbol(b, Decl(lateBoundMutualMemberTables.ts, 1, 13)) +>a : Symbol(B[a.x], Decl(lateBoundMutualMemberTables.ts, 8, 9)) + +export const v3 = a.fromA2; +>v3 : Symbol(v3, Decl(lateBoundMutualMemberTables.ts, 14, 12)) +>a : Symbol(a, Decl(lateBoundMutualMemberTables.ts, 0, 13)) + +export const v4 = b.fromB; +>v4 : Symbol(v4, Decl(lateBoundMutualMemberTables.ts, 15, 12)) +>b.fromB : Symbol(B[a.b], Decl(lateBoundMutualMemberTables.ts, 9, 19)) +>b : Symbol(b, Decl(lateBoundMutualMemberTables.ts, 1, 13)) +>fromB : Symbol(B[a.b], Decl(lateBoundMutualMemberTables.ts, 9, 19)) + diff --git a/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.types b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.types new file mode 100644 index 0000000000000..24f60ac31fc98 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/lateBoundMutualMemberTables.types @@ -0,0 +1,69 @@ +//// [tests/cases/compiler/lateBoundMutualMemberTables.ts] //// + +=== lateBoundMutualMemberTables.ts === +declare const a: A<"a">; +>a : A<"a"> + +declare const b: B<"b">; +>b : B<"b"> + +declare class A { +>A : A + + x: T; +>x : T + + [b.y]: "fromB"; +>[b.y] : "fromB" +>b.y : "b" +>b : B<"b"> +>y : "b" + + [b.by]: "fromB2"; +>[b.by] : "fromB2" +>b.by : any +>b : B<"b"> +>by : any +} +declare class B { +>B : B + + y: T; +>y : T + + [a.x]: "fromA"; +>[a.x] : "fromA" +>a.x : "a" +>a : A<"a"> +>x : "a" + + [a.b]: "fromA2"; +>[a.b] : "fromA2" +>a.b : "fromB" +>a : A<"a"> +>b : "fromB" +} +export const v1 = a.b; +>v1 : "fromB" +>a.b : "fromB" +>a : A<"a"> +>b : "fromB" + +export const v2 = b.a; +>v2 : "fromA" +>b.a : "fromA" +>b : B<"b"> +>a : "fromA" + +export const v3 = a.fromA2; +>v3 : any +>a.fromA2 : any +>a : A<"a"> +>fromA2 : any + +export const v4 = b.fromB; +>v4 : "fromA2" +>b.fromB : "fromA2" +>b : B<"b"> +>fromB : "fromA2" + diff --git a/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=default).errors.txt index a1b033edf54ba..b908bb32df049 100644 --- a/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=default).errors.txt @@ -1,12 +1,15 @@ +limitDeepInstantiations.ts(4,5): error TS5114: Instantiations of type 'Foo' appear infinitely circular. limitDeepInstantiations.ts(4,9): error TS5114: Instantiations of type 'Foo' appear infinitely circular. limitDeepInstantiations.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'. -==== limitDeepInstantiations.ts (2 errors) ==== +==== limitDeepInstantiations.ts (3 errors) ==== // Repro from #14837 type Foo = { "true": Foo> }[T]; let f1: Foo<"true", {}>; + ~~ +!!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. ~~~~~~~~~~~~~~~ !!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. let f2: Foo<"false", {}>; diff --git a/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=typesfirst).errors.txt index 2998e94608985..b908bb32df049 100644 --- a/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/limitDeepInstantiations(checkorder=typesfirst).errors.txt @@ -1,13 +1,17 @@ -error TS5114: Instantiations of type 'Foo' appear infinitely circular. +limitDeepInstantiations.ts(4,5): error TS5114: Instantiations of type 'Foo' appear infinitely circular. +limitDeepInstantiations.ts(4,9): error TS5114: Instantiations of type 'Foo' appear infinitely circular. limitDeepInstantiations.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'. -!!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. -==== limitDeepInstantiations.ts (1 errors) ==== +==== limitDeepInstantiations.ts (3 errors) ==== // Repro from #14837 type Foo = { "true": Foo> }[T]; let f1: Foo<"true", {}>; + ~~ +!!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. + ~~~~~~~~~~~~~~~ +!!! error TS5114: Instantiations of type 'Foo' appear infinitely circular. let f2: Foo<"false", {}>; ~~~~~~~ !!! error TS2344: Type '"false"' does not satisfy the constraint '"true"'. diff --git a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2015).errors.txt b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2015).errors.txt index a8197d380d119..d292daee34b3f 100644 --- a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2015).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2015).errors.txt @@ -5,11 +5,13 @@ malformedDestructuringAssignmentTransformVariants.ts(17,19): error TS2364: The l malformedDestructuringAssignmentTransformVariants.ts(21,24): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(22,28): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS1136: Property assignment expected. +malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +malformedDestructuringAssignmentTransformVariants.ts(23,30): error TS7006: Parameter 'v' implicitly has an 'any' type. malformedDestructuringAssignmentTransformVariants.ts(24,25): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Property assignment expected. -==== malformedDestructuringAssignmentTransformVariants.ts (9 errors) ==== +==== malformedDestructuringAssignmentTransformVariants.ts (11 errors) ==== declare function dec(...args: any[]): any; declare const value: any; @@ -47,6 +49,10 @@ malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Prope ({ x: super.x, set s(v) {}, private: this.#x } = value); ~ !!! error TS1136: Property assignment expected. + ~ +!!! error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~ +!!! error TS7006: Parameter 'v' implicitly has an 'any' type. ({ x: [super.x, 0, this.#x], m() {} } = value); ~ !!! error TS2364: The left-hand side of an assignment expression must be a variable or a property access. diff --git a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2022).errors.txt b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2022).errors.txt index a8197d380d119..d292daee34b3f 100644 --- a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2022).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=false,target=es2022).errors.txt @@ -5,11 +5,13 @@ malformedDestructuringAssignmentTransformVariants.ts(17,19): error TS2364: The l malformedDestructuringAssignmentTransformVariants.ts(21,24): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(22,28): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS1136: Property assignment expected. +malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +malformedDestructuringAssignmentTransformVariants.ts(23,30): error TS7006: Parameter 'v' implicitly has an 'any' type. malformedDestructuringAssignmentTransformVariants.ts(24,25): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Property assignment expected. -==== malformedDestructuringAssignmentTransformVariants.ts (9 errors) ==== +==== malformedDestructuringAssignmentTransformVariants.ts (11 errors) ==== declare function dec(...args: any[]): any; declare const value: any; @@ -47,6 +49,10 @@ malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Prope ({ x: super.x, set s(v) {}, private: this.#x } = value); ~ !!! error TS1136: Property assignment expected. + ~ +!!! error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~ +!!! error TS7006: Parameter 'v' implicitly has an 'any' type. ({ x: [super.x, 0, this.#x], m() {} } = value); ~ !!! error TS2364: The left-hand side of an assignment expression must be a variable or a property access. diff --git a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2015).errors.txt b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2015).errors.txt index eef60bbd5ed70..3cb0cbebf0544 100644 --- a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2015).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2015).errors.txt @@ -6,11 +6,13 @@ malformedDestructuringAssignmentTransformVariants.ts(17,19): error TS2364: The l malformedDestructuringAssignmentTransformVariants.ts(21,24): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(22,28): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS1136: Property assignment expected. +malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +malformedDestructuringAssignmentTransformVariants.ts(23,30): error TS7006: Parameter 'v' implicitly has an 'any' type. malformedDestructuringAssignmentTransformVariants.ts(24,25): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Property assignment expected. -==== malformedDestructuringAssignmentTransformVariants.ts (10 errors) ==== +==== malformedDestructuringAssignmentTransformVariants.ts (12 errors) ==== declare function dec(...args: any[]): any; declare const value: any; @@ -50,6 +52,10 @@ malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Prope ({ x: super.x, set s(v) {}, private: this.#x } = value); ~ !!! error TS1136: Property assignment expected. + ~ +!!! error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~ +!!! error TS7006: Parameter 'v' implicitly has an 'any' type. ({ x: [super.x, 0, this.#x], m() {} } = value); ~ !!! error TS2364: The left-hand side of an assignment expression must be a variable or a property access. diff --git a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2022).errors.txt b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2022).errors.txt index eef60bbd5ed70..3cb0cbebf0544 100644 --- a/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2022).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/malformedDestructuringAssignmentTransformVariants(experimentaldecorators=true,target=es2022).errors.txt @@ -6,11 +6,13 @@ malformedDestructuringAssignmentTransformVariants.ts(17,19): error TS2364: The l malformedDestructuringAssignmentTransformVariants.ts(21,24): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(22,28): error TS1136: Property assignment expected. malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS1136: Property assignment expected. +malformedDestructuringAssignmentTransformVariants.ts(23,28): error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +malformedDestructuringAssignmentTransformVariants.ts(23,30): error TS7006: Parameter 'v' implicitly has an 'any' type. malformedDestructuringAssignmentTransformVariants.ts(24,25): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Property assignment expected. -==== malformedDestructuringAssignmentTransformVariants.ts (10 errors) ==== +==== malformedDestructuringAssignmentTransformVariants.ts (12 errors) ==== declare function dec(...args: any[]): any; declare const value: any; @@ -50,6 +52,10 @@ malformedDestructuringAssignmentTransformVariants.ts(24,38): error TS1136: Prope ({ x: super.x, set s(v) {}, private: this.#x } = value); ~ !!! error TS1136: Property assignment expected. + ~ +!!! error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~ +!!! error TS7006: Parameter 'v' implicitly has an 'any' type. ({ x: [super.x, 0, this.#x], m() {} } = value); ~ !!! error TS2364: The left-hand side of an assignment expression must be a variable or a property access. diff --git a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentClassFields.errors.txt b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentClassFields.errors.txt index 87e4aba639e60..c355d51097e83 100644 --- a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentClassFields.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentClassFields.errors.txt @@ -1,14 +1,17 @@ malformedObjectDestructuringAssignmentClassFields.ts(1,3): error TS1136: Property assignment expected. +malformedObjectDestructuringAssignmentClassFields.ts(1,5): error TS7006: Parameter 'A' implicitly has an 'any' type. malformedObjectDestructuringAssignmentClassFields.ts(1,6): error TS1005: ',' expected. malformedObjectDestructuringAssignmentClassFields.ts(1,7): error TS1127: Invalid character. malformedObjectDestructuringAssignmentClassFields.ts(1,10): error TS1109: Expression expected. malformedObjectDestructuringAssignmentClassFields.ts(2,1): error TS1005: ')' expected. -==== malformedObjectDestructuringAssignmentClassFields.ts (5 errors) ==== +==== malformedObjectDestructuringAssignmentClassFields.ts (6 errors) ==== ({x(A.#}= ~ !!! error TS1136: Property assignment expected. + ~ +!!! error TS7006: Parameter 'A' implicitly has an 'any' type. ~ !!! error TS1005: ',' expected. ~ diff --git a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt index e398726486b0a..a8cd58532857e 100644 --- a/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/malformedObjectDestructuringAssignmentESDecorators.errors.txt @@ -1,9 +1,11 @@ malformedObjectDestructuringAssignmentESDecorators.ts(6,12): error TS1136: Property assignment expected. malformedObjectDestructuringAssignmentESDecorators.ts(6,24): error TS1136: Property assignment expected. malformedObjectDestructuringAssignmentESDecorators.ts(6,47): error TS1136: Property assignment expected. +malformedObjectDestructuringAssignmentESDecorators.ts(6,47): error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. +malformedObjectDestructuringAssignmentESDecorators.ts(6,49): error TS7006: Parameter 'v' implicitly has an 'any' type. -==== malformedObjectDestructuringAssignmentESDecorators.ts (3 errors) ==== +==== malformedObjectDestructuringAssignmentESDecorators.ts (5 errors) ==== declare function dec(...args: any[]): any; @dec @@ -16,6 +18,10 @@ malformedObjectDestructuringAssignmentESDecorators.ts(6,47): error TS1136: Prope !!! error TS1136: Property assignment expected. ~ !!! error TS1136: Property assignment expected. + ~ +!!! error TS7032: Property 's' implicitly has type 'any', because its set accessor lacks a parameter type annotation. + ~ +!!! error TS7006: Parameter 'v' implicitly has an 'any' type. } } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt index f9e0d6ab4eac5..bff4827ef673c 100644 --- a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.errors.txt @@ -1,4 +1,4 @@ -/main.ts(7,32): error TS2339: Property 'mb' does not exist on type '{}'. +/main.ts(8,32): error TS2339: Property 'ma' does not exist on type '{}'. ==== /main.ts (1 errors) ==== @@ -9,9 +9,9 @@ export const fromA = MA.pa; export const fromB = MB.pb; export const checkA: 1 = MA.pa.mb; - ~~ -!!! error TS2339: Property 'mb' does not exist on type '{}'. export const checkB: 1 = MB.pb.ma; + ~~ +!!! error TS2339: Property 'ma' does not exist on type '{}'. ==== /ma.js (0 errors) ==== export { v as pa } from "./xa"; diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js index b99218d5bfd65..e7f345491f402 100644 --- a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.js @@ -185,7 +185,10 @@ exports.checkB = MB.pb.ma; //// [xa.d.ts] export = o; declare const o: { - v: {}; + v: { + pb: {}; + mb: 1; + }; }; //// [ma.d.ts] export { v as pa } from "./xa"; @@ -193,10 +196,7 @@ export declare const ma = 1; //// [xb.d.ts] export = o; declare const o: { - v: { - pa: {}; - ma: 1; - }; + v: {}; }; //// [mb.d.ts] export { v as pb } from "./xb"; @@ -205,10 +205,7 @@ export declare const mb = 1; export = o; declare const o: { v: { - pb: { - pa: {}; - ma: 1; - }; + pb: {}; mb: 1; }; }; @@ -216,7 +213,10 @@ declare const o: { export = o; declare const o: { v: { - pa: {}; + pa: { + pb: {}; + mb: 1; + }; ma: 1; }; }; @@ -236,10 +236,10 @@ export declare const f8 = 8; export declare const f9 = 9; export declare const f10 = 10; //// [main.d.ts] -export declare const fromA: {}; -export declare const fromB: { - pa: {}; - ma: 1; +export declare const fromA: { + pb: {}; + mb: 1; }; +export declare const fromB: {}; export declare const checkA: 1; export declare const checkB: 1; diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols index 4f7c5a9b9f1d4..6997e4d5a273d 100644 --- a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.symbols @@ -28,17 +28,17 @@ export const fromB = MB.pb; export const checkA: 1 = MA.pa.mb; >checkA : Symbol(checkA, Decl(main.ts, 6, 12)) +>MA.pa.mb : Symbol(mb, Decl(mb.js, 1, 12)) >MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) >MA : Symbol(MA, Decl(main.ts, 2, 6)) >pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) +>mb : Symbol(mb, Decl(mb.js, 1, 12)) export const checkB: 1 = MB.pb.ma; >checkB : Symbol(checkB, Decl(main.ts, 7, 12)) ->MB.pb.ma : Symbol(ma, Decl(ma.js, 1, 12)) >MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) >MB : Symbol(MB, Decl(main.ts, 3, 6)) >pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) ->ma : Symbol(ma, Decl(ma.js, 1, 12)) === /ma.js === export { v as pa } from "./xa"; diff --git a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types index 0153880cec8d7..49f1de0b3ac44 100644 --- a/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types +++ b/tsc/testdata/baselines/reference/compiler/namespaceMembersResolutionOrderExportStar.types @@ -5,8 +5,8 @@ import * as idx from "./index"; >idx : typeof idx const all = { ...idx }; ->all : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } ->{ ...idx } : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>all : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>{ ...idx } : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } >idx : typeof idx import * as MA from "./ma"; @@ -16,37 +16,37 @@ import * as MB from "./mb"; >MB : typeof MB export const fromA = MA.pa; ->fromA : {} ->MA.pa : {} +>fromA : { pb: {}; mb: 1; } +>MA.pa : { pb: {}; mb: 1; } >MA : typeof MA ->pa : {} +>pa : { pb: {}; mb: 1; } export const fromB = MB.pb; ->fromB : { pa: {}; ma: 1; } ->MB.pb : { pa: {}; ma: 1; } +>fromB : {} +>MB.pb : {} >MB : typeof MB ->pb : { pa: {}; ma: 1; } +>pb : {} export const checkA: 1 = MA.pa.mb; >checkA : 1 ->MA.pa.mb : any ->MA.pa : {} +>MA.pa.mb : 1 +>MA.pa : { pb: {}; mb: 1; } >MA : typeof MA ->pa : {} ->mb : any +>pa : { pb: {}; mb: 1; } +>mb : 1 export const checkB: 1 = MB.pb.ma; >checkB : 1 ->MB.pb.ma : 1 ->MB.pb : { pa: {}; ma: 1; } +>MB.pb.ma : any +>MB.pb : {} >MB : typeof MB ->pb : { pa: {}; ma: 1; } ->ma : 1 +>pb : {} +>ma : any === /ma.js === export { v as pa } from "./xa"; ->v : {} ->pa : {} +>v : { pb: {}; mb: 1; } +>pa : { pb: {}; mb: 1; } export const ma = 1; >ma : 1 @@ -54,8 +54,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : { pa: {}; ma: 1; } ->pb : { pa: {}; ma: 1; } +>v : {} +>pb : {} export const mb = 1; >mb : 1 @@ -68,18 +68,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>o : { v: { pb: {}; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } +>v : { pb: {}; mb: 1; } +>{ ...M } : { pb: {}; mb: 1; } >M : typeof import("./mb") module.exports = o; ->module.exports = o : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->o : { v: {}; } +>module.exports = o : { v: { pb: {}; mb: 1; }; } +>module.exports : { v: { pb: {}; mb: 1; }; } +>module : { exports: { v: { pb: {}; mb: 1; }; }; } +>exports : { v: { pb: {}; mb: 1; }; } +>o : { v: { pb: {}; mb: 1; }; } === /xb.js === /** @type {typeof import("./ma")} */ @@ -88,18 +88,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pa: {}; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } ->v : { pa: {}; ma: 1; } ->{ ...M } : { pa: {}; ma: 1; } +>o : { v: {}; } +>{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } >M : typeof import("./ma") module.exports = o; ->module.exports = o : { v: { pa: {}; ma: 1; }; } ->module.exports : { v: { pa: {}; ma: 1; }; } ->module : { exports: { v: { pa: {}; ma: 1; }; }; } ->exports : { v: { pa: {}; ma: 1; }; } ->o : { v: { pa: {}; ma: 1; }; } +>module.exports = o : { v: {}; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>o : { v: {}; } === /ia.js === /** @type {typeof import("./ma")} */ @@ -108,18 +108,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pa: {}; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } ->v : { pa: {}; ma: 1; } ->{ ...M } : { pa: {}; ma: 1; } +>o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } >M : typeof import("./ma") module.exports = o; ->module.exports = o : { v: { pa: {}; ma: 1; }; } ->module.exports : { v: { pa: {}; ma: 1; }; } ->module : { exports: { v: { pa: {}; ma: 1; }; }; } ->exports : { v: { pa: {}; ma: 1; }; } ->o : { v: { pa: {}; ma: 1; }; } +>module.exports = o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } +>exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } === /ib.js === /** @type {typeof import("./mb")} */ @@ -128,28 +128,28 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } ->v : { pb: { pa: {}; ma: 1; }; mb: 1; } ->{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } +>o : { v: { pb: {}; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } +>v : { pb: {}; mb: 1; } +>{ ...M } : { pb: {}; mb: 1; } >M : typeof import("./mb") module.exports = o; ->module.exports = o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } ->module.exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } ->module : { exports: { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; }; } ->exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } ->o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports = o : { v: { pb: {}; mb: 1; }; } +>module.exports : { v: { pb: {}; mb: 1; }; } +>module : { exports: { v: { pb: {}; mb: 1; }; }; } +>exports : { v: { pb: {}; mb: 1; }; } +>o : { v: { pb: {}; mb: 1; }; } === /star.js === export { v as a } from "./ia"; ->v : { pa: {}; ma: 1; } ->a : { pa: {}; ma: 1; } +>v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>a : { pa: { pb: {}; mb: 1; }; ma: 1; } === /index.js === export { v as b } from "./ib"; ->v : { pb: { pa: {}; ma: 1; }; mb: 1; } ->b : { pb: { pa: {}; ma: 1; }; mb: 1; } +>v : { pb: {}; mb: 1; } +>b : { pb: {}; mb: 1; } export * from "./star"; export const f1 = 1; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js index 2413425ee0027..613464e8a9ce9 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.js @@ -78,5 +78,7 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types index 6e3734d808ac2..d2fb088ff22ab 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExportEqualsQualifiedNameOrder.types @@ -22,8 +22,8 @@ export const obsA = expand(); >oxa : O export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js index b04d7f8d88c86..06233f3cd3615 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.js @@ -77,5 +77,7 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types index 351c0f5869478..236cd6a2af6c0 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitExternalModuleContainerIndexOrder.types @@ -26,8 +26,8 @@ export const obsA = expand(); >expand : () => { [K in keyof X]: X[K]; } export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } === /ambient.d.ts === diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js index da41b8a0dc439..09f23b9e441c9 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.js @@ -90,6 +90,8 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; export {}; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types index 9ca145132f072..a3e841f76db76 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitLateBoundIndexSignatureSiblingsOrder.types @@ -82,8 +82,8 @@ export const obsA = expand(); >oxa : O export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js index ea5bdabd550b2..593f5c6192e79 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.js @@ -75,5 +75,7 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types index ad400806b641d..59c394c4943b9 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitRenamedExportOrder.types @@ -21,8 +21,8 @@ export const obsA = expand(); >oxa : O export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitSharedCheckerOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitSharedCheckerOrder.types index 07b1f23ab8990..cde44436bf6e0 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitSharedCheckerOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitSharedCheckerOrder.types @@ -26,9 +26,9 @@ const M = /** @type {any} */ (null); const o = { v: { ...M } }; >o : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") module.exports = o; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js index f297255bfad65..0cd47efb71bf9 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.js @@ -139,5 +139,7 @@ declare const obsA: { v: 0; }; declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types index cb6e79ef1518c..64131b7405d04 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitUmdGlobalAliasesOrder.types @@ -34,8 +34,8 @@ const obsA = expand(); >oxa : O const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js index 30a76df171c66..b4b3b1191b09c 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.js @@ -55,5 +55,7 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types index 2e41e20ad32be..74c433948a034 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDeclarationEmitVariableMatchesInScopeOrder.types @@ -19,8 +19,8 @@ export const obsA = expand(); >oxa : O export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt index a2ecba905fe18..62a7166294745 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.errors.txt @@ -1,5 +1,5 @@ /main.ts(7,14): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. -/main.ts(8,14): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. +/main.ts(8,14): error TS2322: Type '{ pa: 0; readonly ma: 1; v: 0; }' is not assignable to type 'never'. ==== /main.ts (2 errors) ==== @@ -14,7 +14,7 @@ !!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. export const obsB: never = expand(); ~~~~ -!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. +!!! error TS2322: Type '{ pa: 0; readonly ma: 1; v: 0; }' is not assignable to type 'never'. ==== /ambient.d.ts (0 errors) ==== declare module "ma" { diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types index 925b972b915c6..b42d11f662f49 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarCollisionOrder.types @@ -26,7 +26,7 @@ export const obsA: never = expand(); export const obsB: never = expand(); >obsB : never ->expand() : { v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt index a05bda29acf17..45ba492a4b355 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.errors.txt @@ -1,5 +1,5 @@ /main.ts(7,14): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. -/main.ts(8,14): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. +/main.ts(8,14): error TS2322: Type '{ pa: 0; readonly ma: 1; v: 0; }' is not assignable to type 'never'. ==== /main.ts (2 errors) ==== @@ -14,7 +14,7 @@ !!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. export const obsB: never = expand(); ~~~~ -!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. +!!! error TS2322: Type '{ pa: 0; readonly ma: 1; v: 0; }' is not assignable to type 'never'. ==== /ambient.d.ts (0 errors) ==== declare module "ma" { diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types index 58541f8aa49ff..101b6db6c4dee 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsExportStarDeclarationCollisionOrder.types @@ -26,7 +26,7 @@ export const obsA: never = expand(); export const obsB: never = expand(); >obsB : never ->expand() : { v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt index fb286e6d925c7..a15ea5b841fce 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.errors.txt @@ -1,4 +1,4 @@ -/main.ts(8,32): error TS2339: Property 'ma' does not exist on type '{}'. +/main.ts(7,32): error TS2339: Property 'mb' does not exist on type '{}'. ==== /main.ts (1 errors) ==== @@ -9,9 +9,9 @@ export const fromA = MA.pa; export const fromB = MB.pb; export const checkA: 1 = MA.pa.mb; - export const checkB: 1 = MB.pb.ma; ~~ -!!! error TS2339: Property 'ma' does not exist on type '{}'. +!!! error TS2339: Property 'mb' does not exist on type '{}'. + export const checkB: 1 = MB.pb.ma; ==== /ma.js (0 errors) ==== export { v as pa } from "./xa"; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js index 85b0024d3067d..4881af4a1d031 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.js @@ -163,7 +163,10 @@ exports.checkB = MB.pb.ma; //// [xb.d.ts] export = o; declare const o: { - v: {}; + v: { + pa: {}; + ma: 1; + }; }; //// [mb.d.ts] export { v as pb } from "./xb"; @@ -171,10 +174,7 @@ export declare const mb = 1; //// [xa.d.ts] export = o; declare const o: { - v: { - pb: {}; - mb: 1; - }; + v: {}; }; //// [ma.d.ts] export { v as pa } from "./xa"; @@ -183,10 +183,7 @@ export declare const ma = 1; export = o; declare const o: { v: { - pa: { - pb: {}; - mb: 1; - }; + pa: {}; ma: 1; }; }; @@ -194,7 +191,10 @@ declare const o: { export = o; declare const o: { v: { - pb: {}; + pb: { + pa: {}; + ma: 1; + }; mb: 1; }; }; @@ -212,10 +212,10 @@ export declare const f8 = 8; export declare const f9 = 9; export declare const f10 = 10; //// [main.d.ts] -export declare const fromA: { - pb: {}; - mb: 1; +export declare const fromA: {}; +export declare const fromB: { + pa: {}; + ma: 1; }; -export declare const fromB: {}; export declare const checkA: 1; export declare const checkB: 1; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols index aaa25b0469d2f..74e3cd92e4248 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.symbols @@ -28,17 +28,17 @@ export const fromB = MB.pb; export const checkA: 1 = MA.pa.mb; >checkA : Symbol(checkA, Decl(main.ts, 6, 12)) ->MA.pa.mb : Symbol(mb, Decl(mb.js, 1, 12)) >MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) >MA : Symbol(MA, Decl(main.ts, 2, 6)) >pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) ->mb : Symbol(mb, Decl(mb.js, 1, 12)) export const checkB: 1 = MB.pb.ma; >checkB : Symbol(checkB, Decl(main.ts, 7, 12)) +>MB.pb.ma : Symbol(ma, Decl(ma.js, 1, 12)) >MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) >MB : Symbol(MB, Decl(main.ts, 3, 6)) >pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>ma : Symbol(ma, Decl(ma.js, 1, 12)) === /ma.js === export { v as pa } from "./xa"; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types index 753e075741287..f7142131ca798 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrder.types @@ -5,8 +5,8 @@ import * as idx from "./index"; >idx : typeof idx const all = { ...idx }; ->all : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } ->{ ...idx } : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>all : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>{ ...idx } : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } >idx : typeof idx import * as MA from "./ma"; @@ -16,37 +16,37 @@ import * as MB from "./mb"; >MB : typeof MB export const fromA = MA.pa; ->fromA : { pb: {}; mb: 1; } ->MA.pa : { pb: {}; mb: 1; } +>fromA : {} +>MA.pa : {} >MA : typeof MA ->pa : { pb: {}; mb: 1; } +>pa : {} export const fromB = MB.pb; ->fromB : {} ->MB.pb : {} +>fromB : { pa: {}; ma: 1; } +>MB.pb : { pa: {}; ma: 1; } >MB : typeof MB ->pb : {} +>pb : { pa: {}; ma: 1; } export const checkA: 1 = MA.pa.mb; >checkA : 1 ->MA.pa.mb : 1 ->MA.pa : { pb: {}; mb: 1; } +>MA.pa.mb : any +>MA.pa : {} >MA : typeof MA ->pa : { pb: {}; mb: 1; } ->mb : 1 +>pa : {} +>mb : any export const checkB: 1 = MB.pb.ma; >checkB : 1 ->MB.pb.ma : any ->MB.pb : {} +>MB.pb.ma : 1 +>MB.pb : { pa: {}; ma: 1; } >MB : typeof MB ->pb : {} ->ma : any +>pb : { pa: {}; ma: 1; } +>ma : 1 === /ma.js === export { v as pa } from "./xa"; ->v : { pb: {}; mb: 1; } ->pa : { pb: {}; mb: 1; } +>v : {} +>pa : {} export const ma = 1; >ma : 1 @@ -54,8 +54,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : {} ->pb : {} +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } export const mb = 1; >mb : 1 @@ -68,18 +68,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>o : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") module.exports = o; ->module.exports = o : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->o : { v: { pb: {}; mb: 1; }; } +>module.exports = o : { v: {}; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>o : { v: {}; } === /xb.js === /** @type {typeof import("./ma")} */ @@ -88,18 +88,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>o : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") module.exports = o; ->module.exports = o : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->o : { v: {}; } +>module.exports = o : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>o : { v: { pa: {}; ma: 1; }; } === /ia.js === /** @type {typeof import("./ma")} */ @@ -108,18 +108,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>o : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") module.exports = o; ->module.exports = o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } ->exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module.exports = o : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>o : { v: { pa: {}; ma: 1; }; } === /ib.js === /** @type {typeof import("./mb")} */ @@ -128,27 +128,27 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") module.exports = o; ->module.exports = o : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->o : { v: { pb: {}; mb: 1; }; } +>module.exports = o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module : { exports: { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; }; } +>exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } === /index.js === export { v as a } from "./ia"; ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->a : { pa: { pb: {}; mb: 1; }; ma: 1; } +>v : { pa: {}; ma: 1; } +>a : { pa: {}; ma: 1; } export { v as b } from "./ib"; ->v : { pb: {}; mb: 1; } ->b : { pb: {}; mb: 1; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>b : { pb: { pa: {}; ma: 1; }; mb: 1; } export const f1 = 1; >f1 : 1 diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt index 408176b1c59d1..c3d47c451cd56 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.errors.txt @@ -1,4 +1,4 @@ -/main.ts(8,32): error TS2339: Property 'ma' does not exist on type '{}'. +/main.ts(7,32): error TS2339: Property 'mb' does not exist on type '{}'. ==== /main.ts (1 errors) ==== @@ -9,9 +9,9 @@ export const fromA = MA.pa; export const fromB = MB.pb; export const checkA: 1 = MA.pa.mb; - export const checkB: 1 = MB.pb.ma; ~~ -!!! error TS2339: Property 'ma' does not exist on type '{}'. +!!! error TS2339: Property 'mb' does not exist on type '{}'. + export const checkB: 1 = MB.pb.ma; ==== /ma.js (0 errors) ==== export { v as pa } from "./xa"; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js index beb6f57cf711b..78caebf29bfbb 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.js @@ -161,7 +161,10 @@ exports.checkB = MB.pb.ma; //// [xb.d.ts] export = o; declare const o: { - v: {}; + v: { + pa: {}; + ma: 1; + }; }; //// [mb.d.ts] export { v as pb } from "./xb"; @@ -169,10 +172,7 @@ export declare const mb = 1; //// [xa.d.ts] export = o; declare const o: { - v: { - pb: {}; - mb: 1; - }; + v: {}; }; //// [ma.d.ts] export { v as pa } from "./xa"; @@ -181,10 +181,7 @@ export declare const ma = 1; export = o; declare const o: { v: { - pa: { - pb: {}; - mb: 1; - }; + pa: {}; ma: 1; }; }; @@ -192,15 +189,18 @@ declare const o: { export = o; declare const o: { v: { - pb: {}; + pb: { + pa: {}; + ma: 1; + }; mb: 1; }; }; //// [main.d.ts] -export declare const fromA: { - pb: {}; - mb: 1; +export declare const fromA: {}; +export declare const fromB: { + pa: {}; + ma: 1; }; -export declare const fromB: {}; export declare const checkA: 1; export declare const checkB: 1; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols index e737ff2cbfb8a..5d199dbcf8abb 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.symbols @@ -28,17 +28,17 @@ export const fromB = MB.pb; export const checkA: 1 = MA.pa.mb; >checkA : Symbol(checkA, Decl(main.ts, 6, 12)) ->MA.pa.mb : Symbol(mb, Decl(mb.js, 1, 12)) >MA.pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) >MA : Symbol(MA, Decl(main.ts, 2, 6)) >pa : Symbol(MA.pa, Decl(ma.js, 0, 8)) ->mb : Symbol(mb, Decl(mb.js, 1, 12)) export const checkB: 1 = MB.pb.ma; >checkB : Symbol(checkB, Decl(main.ts, 7, 12)) +>MB.pb.ma : Symbol(ma, Decl(ma.js, 1, 12)) >MB.pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) >MB : Symbol(MB, Decl(main.ts, 3, 6)) >pb : Symbol(MB.pb, Decl(mb.js, 0, 8)) +>ma : Symbol(ma, Decl(ma.js, 1, 12)) === /ma.js === export { v as pa } from "./xa"; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types index 53a815348e451..7354cbcd52d6f 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNamespaceMembersResolutionOrderReexportedTypes.types @@ -5,8 +5,8 @@ import * as idx from "./d"; >idx : typeof idx const all = { ...idx }; ->all : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } ->{ ...idx } : { a: { pa: { pb: {}; mb: 1; }; ma: 1; }; b: { pb: {}; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>all : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } +>{ ...idx } : { a: { pa: {}; ma: 1; }; b: { pb: { pa: {}; ma: 1; }; mb: 1; }; f1: 1; f2: 2; f3: 3; f4: 4; f5: 5; f6: 6; f7: 7; f8: 8; f9: 9; f10: 10; } >idx : typeof idx import * as MA from "./ma"; @@ -16,37 +16,37 @@ import * as MB from "./mb"; >MB : typeof MB export const fromA = MA.pa; ->fromA : { pb: {}; mb: 1; } ->MA.pa : { pb: {}; mb: 1; } +>fromA : {} +>MA.pa : {} >MA : typeof MA ->pa : { pb: {}; mb: 1; } +>pa : {} export const fromB = MB.pb; ->fromB : {} ->MB.pb : {} +>fromB : { pa: {}; ma: 1; } +>MB.pb : { pa: {}; ma: 1; } >MB : typeof MB ->pb : {} +>pb : { pa: {}; ma: 1; } export const checkA: 1 = MA.pa.mb; >checkA : 1 ->MA.pa.mb : 1 ->MA.pa : { pb: {}; mb: 1; } +>MA.pa.mb : any +>MA.pa : {} >MA : typeof MA ->pa : { pb: {}; mb: 1; } ->mb : 1 +>pa : {} +>mb : any export const checkB: 1 = MB.pb.ma; >checkB : 1 ->MB.pb.ma : any ->MB.pb : {} +>MB.pb.ma : 1 +>MB.pb : { pa: {}; ma: 1; } >MB : typeof MB ->pb : {} ->ma : any +>pb : { pa: {}; ma: 1; } +>ma : 1 === /ma.js === export { v as pa } from "./xa"; ->v : { pb: {}; mb: 1; } ->pa : { pb: {}; mb: 1; } +>v : {} +>pa : {} export const ma = 1; >ma : 1 @@ -54,8 +54,8 @@ export const ma = 1; === /mb.js === export { v as pb } from "./xb"; ->v : {} ->pb : {} +>v : { pa: {}; ma: 1; } +>pb : { pa: {}; ma: 1; } export const mb = 1; >mb : 1 @@ -68,18 +68,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>o : { v: {}; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") module.exports = o; ->module.exports = o : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->o : { v: { pb: {}; mb: 1; }; } +>module.exports = o : { v: {}; } +>module.exports : { v: {}; } +>module : { exports: { v: {}; }; } +>exports : { v: {}; } +>o : { v: {}; } === /xb.js === /** @type {typeof import("./ma")} */ @@ -88,18 +88,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: {}; } ->{ v: { ...M } } : { v: {}; } ->v : {} ->{ ...M } : {} +>o : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") module.exports = o; ->module.exports = o : { v: {}; } ->module.exports : { v: {}; } ->module : { exports: { v: {}; }; } ->exports : { v: {}; } ->o : { v: {}; } +>module.exports = o : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>o : { v: { pa: {}; ma: 1; }; } === /ia.js === /** @type {typeof import("./ma")} */ @@ -108,18 +108,18 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->{ v: { ...M } } : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->{ ...M } : { pa: { pb: {}; mb: 1; }; ma: 1; } +>o : { v: { pa: {}; ma: 1; }; } +>{ v: { ...M } } : { v: { pa: {}; ma: 1; }; } +>v : { pa: {}; ma: 1; } +>{ ...M } : { pa: {}; ma: 1; } >M : typeof import("./ma") module.exports = o; ->module.exports = o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module.exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->module : { exports: { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; }; } ->exports : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } ->o : { v: { pa: { pb: {}; mb: 1; }; ma: 1; }; } +>module.exports = o : { v: { pa: {}; ma: 1; }; } +>module.exports : { v: { pa: {}; ma: 1; }; } +>module : { exports: { v: { pa: {}; ma: 1; }; }; } +>exports : { v: { pa: {}; ma: 1; }; } +>o : { v: { pa: {}; ma: 1; }; } === /ib.js === /** @type {typeof import("./mb")} */ @@ -128,41 +128,41 @@ const M = /** @type {any} */ (null); >(null) : any const o = { v: { ...M } }; ->o : { v: { pb: {}; mb: 1; }; } ->{ v: { ...M } } : { v: { pb: {}; mb: 1; }; } ->v : { pb: {}; mb: 1; } ->{ ...M } : { pb: {}; mb: 1; } +>o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>{ v: { ...M } } : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>{ ...M } : { pb: { pa: {}; ma: 1; }; mb: 1; } >M : typeof import("./mb") module.exports = o; ->module.exports = o : { v: { pb: {}; mb: 1; }; } ->module.exports : { v: { pb: {}; mb: 1; }; } ->module : { exports: { v: { pb: {}; mb: 1; }; }; } ->exports : { v: { pb: {}; mb: 1; }; } ->o : { v: { pb: {}; mb: 1; }; } +>module.exports = o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module.exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>module : { exports: { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; }; } +>exports : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } +>o : { v: { pb: { pa: {}; ma: 1; }; mb: 1; }; } === /ia2.d.ts === export { v } from "./ia"; ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } +>v : { pa: {}; ma: 1; } export type v = number; >v : number === /ib2.d.ts === export { v } from "./ib"; ->v : { pb: {}; mb: 1; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } export type v = number; >v : number === /index.d.ts === export { v as a } from "./ia2"; ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->a : { pa: { pb: {}; mb: 1; }; ma: 1; } +>v : { pa: {}; ma: 1; } +>a : { pa: {}; ma: 1; } export { v as b } from "./ib2"; ->v : { pb: {}; mb: 1; } ->b : { pb: {}; mb: 1; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>b : { pb: { pa: {}; ma: 1; }; mb: 1; } export const f1: 1; >f1 : 1 @@ -196,12 +196,12 @@ export const f10: 10; === /index2.d.ts === export { v as a } from "./ia2"; ->v : { pa: { pb: {}; mb: 1; }; ma: 1; } ->a : { pa: { pb: {}; mb: 1; }; ma: 1; } +>v : { pa: {}; ma: 1; } +>a : { pa: {}; ma: 1; } export { v as b } from "./ib2"; ->v : { pb: {}; mb: 1; } ->b : { pb: {}; mb: 1; } +>v : { pb: { pa: {}; ma: 1; }; mb: 1; } +>b : { pb: { pa: {}; ma: 1; }; mb: 1; } === /d.d.ts === diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt index aba9bd814f87b..9d0c5d89f5be4 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.errors.txt @@ -1,6 +1,6 @@ /main.ts(2,10): error TS2459: Module '"mt"' declares 'zz' locally, but it is not exported. /main.ts(6,14): error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. -/main.ts(7,14): error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. +/main.ts(7,14): error TS2322: Type '{ pa: 0; readonly ma: 1; v: 0; }' is not assignable to type 'never'. ==== /main.ts (3 errors) ==== @@ -17,7 +17,7 @@ !!! error TS2322: Type '{ pb: 0; readonly mb: 1; v: 0; }' is not assignable to type 'never'. export const obsB: never = expand(); ~~~~ -!!! error TS2322: Type '{ v: 0; }' is not assignable to type 'never'. +!!! error TS2322: Type '{ pa: 0; readonly ma: 1; v: 0; }' is not assignable to type 'never'. ==== /ambient.d.ts (0 errors) ==== declare module "ma" { diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types index 00ba3854da277..ce4272bde5283 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyDiagnosticsNonExportedMemberOrder.types @@ -22,7 +22,7 @@ export const obsA: never = expand(); export const obsB: never = expand(); >obsB : never ->expand() : { v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt index d81eb346f2055..c291bb9504fc7 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.errors.txt @@ -1,8 +1,7 @@ /mm.ts(5,1): error TS2309: An export assignment cannot be used in a module with other exported elements. -/mm.ts(19,30): error TS2339: Property 'pa' does not exist on type 'O'. -==== /mm.ts (2 errors) ==== +==== /mm.ts (1 errors) ==== /// declare const oxa: typeof import("xa"); declare const oxb: typeof import("xb"); @@ -24,8 +23,6 @@ export interface F10 {} export const checkA: 0 = oxa.pb; export const checkB: 0 = oxb.pa; - ~~ -!!! error TS2339: Property 'pa' does not exist on type 'O'. ==== /ambient.d.ts (0 errors) ==== declare module "ma" { diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols index 74a9fa86901eb..dc3daa982e602 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.symbols @@ -61,7 +61,9 @@ export const checkA: 0 = oxa.pb; export const checkB: 0 = oxb.pa; >checkB : Symbol(checkB, Decl(mm.ts, 18, 12)) +>oxb.pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) >oxb : Symbol(oxb, Decl(mm.ts, 2, 13)) +>pa : Symbol(pa, Decl(ambient.d.ts, 1, 12)) === /ambient.d.ts === declare module "ma" { diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types index 44664dcadfb80..d50ab18d6826e 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsCheckOrder.types @@ -41,9 +41,9 @@ export const checkA: 0 = oxa.pb; export const checkB: 0 = oxb.pa; >checkB : 0 ->oxb.pa : any +>oxb.pa : 0 >oxb : O ->pa : any +>pa : 0 === /ambient.d.ts === declare module "ma" { diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js index 37e7a9ff94890..527e0cf1c555a 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.js @@ -71,5 +71,7 @@ export declare const obsA: { v: 0; }; export declare const obsB: { + pa: 0; + readonly ma: 1; v: 0; }; diff --git a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types index c0ba114c78355..03579880e27c4 100644 --- a/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types +++ b/tsc/testdata/baselines/reference/compiler/noFlakyExportEqualsWithOtherExportsImportOrder.types @@ -25,8 +25,8 @@ export const obsA = expand(); >oxa : O export const obsB = expand(); ->obsB : { v: 0; } ->expand() : { v: 0; } +>obsB : { pa: 0; readonly ma: 1; v: 0; } +>expand() : { pa: 0; readonly ma: 1; v: 0; } >expand : () => { [K in keyof X]: X[K]; } >oxb : O diff --git a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).errors.txt index fab2d38147e93..28167de70ad61 100644 --- a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).errors.txt @@ -1,9 +1,10 @@ parameterBindingDefaultReferencesSiblingThroughCall.ts(11,19): error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. -parameterBindingDefaultReferencesSiblingThroughCall.ts(16,11): error TS2322: Type 'unknown' is not assignable to type 'string'. +parameterBindingDefaultReferencesSiblingThroughCall.ts(15,20): error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +parameterBindingDefaultReferencesSiblingThroughCall.ts(19,25): error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. parameterBindingDefaultReferencesSiblingThroughCall.ts(20,11): error TS2322: Type 'any[]' is not assignable to type 'string'. -==== parameterBindingDefaultReferencesSiblingThroughCall.ts (3 errors) ==== +==== parameterBindingDefaultReferencesSiblingThroughCall.ts (4 errors) ==== // A default in a destructured parameter that references a sibling element // makes the sibling's type depend on itself: the type of 'a' comes from the // parameter initializer '{}', which is padded with the types of the defaults, @@ -21,12 +22,14 @@ parameterBindingDefaultReferencesSiblingThroughCall.ts(20,11): error TS2322: Typ } function viaCall({ a = 1 as number | undefined, b = id(a) } = {}) { + ~ +!!! error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. const n: string = b; - ~ -!!! error TS2322: Type 'unknown' is not assignable to type 'string'. } function viaCallArray({ a = 1 as number | undefined, b = id([a]) } = {}) { + ~ +!!! error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. const n: string = b; ~ !!! error TS2322: Type 'any[]' is not assignable to type 'string'. diff --git a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).types b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).types index d8b5d78c1e02b..7c1aeb46582c6 100644 --- a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).types +++ b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=default).types @@ -28,31 +28,31 @@ function direct({ a = 1 as number | undefined, b = a } = {}) { } function viaCall({ a = 1 as number | undefined, b = id(a) } = {}) { ->viaCall : ({ a, b }?: { a?: number | undefined; b?: unknown; }) => void ->a : number | undefined +>viaCall : ({ a, b }?: { a?: number | undefined; b?: any; }) => void +>a : any >1 as number | undefined : number | undefined >1 : 1 ->b : unknown ->id(a) : unknown +>b : any +>id(a) : any >id : (v: T) => T ->a : number | undefined +>a : any >{} : {} const n: string = b; >n : string ->b : unknown +>b : any } function viaCallArray({ a = 1 as number | undefined, b = id([a]) } = {}) { >viaCallArray : ({ a, b }?: { a?: number | undefined; b?: any[] | undefined; }) => void ->a : number | undefined +>a : any >1 as number | undefined : number | undefined >1 : 1 >b : any[] >id([a]) : any[] >id : (v: T) => T ->[a] : (number | undefined)[] ->a : number | undefined +>[a] : any[] +>a : any >{} : {} const n: string = b; diff --git a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).errors.txt index fab2d38147e93..28167de70ad61 100644 --- a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).errors.txt @@ -1,9 +1,10 @@ parameterBindingDefaultReferencesSiblingThroughCall.ts(11,19): error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. -parameterBindingDefaultReferencesSiblingThroughCall.ts(16,11): error TS2322: Type 'unknown' is not assignable to type 'string'. +parameterBindingDefaultReferencesSiblingThroughCall.ts(15,20): error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +parameterBindingDefaultReferencesSiblingThroughCall.ts(19,25): error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. parameterBindingDefaultReferencesSiblingThroughCall.ts(20,11): error TS2322: Type 'any[]' is not assignable to type 'string'. -==== parameterBindingDefaultReferencesSiblingThroughCall.ts (3 errors) ==== +==== parameterBindingDefaultReferencesSiblingThroughCall.ts (4 errors) ==== // A default in a destructured parameter that references a sibling element // makes the sibling's type depend on itself: the type of 'a' comes from the // parameter initializer '{}', which is padded with the types of the defaults, @@ -21,12 +22,14 @@ parameterBindingDefaultReferencesSiblingThroughCall.ts(20,11): error TS2322: Typ } function viaCall({ a = 1 as number | undefined, b = id(a) } = {}) { + ~ +!!! error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. const n: string = b; - ~ -!!! error TS2322: Type 'unknown' is not assignable to type 'string'. } function viaCallArray({ a = 1 as number | undefined, b = id([a]) } = {}) { + ~ +!!! error TS7022: 'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. const n: string = b; ~ !!! error TS2322: Type 'any[]' is not assignable to type 'string'. diff --git a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).types index d8b5d78c1e02b..7c1aeb46582c6 100644 --- a/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/compiler/parameterBindingDefaultReferencesSiblingThroughCall(checkorder=typesfirst).types @@ -28,31 +28,31 @@ function direct({ a = 1 as number | undefined, b = a } = {}) { } function viaCall({ a = 1 as number | undefined, b = id(a) } = {}) { ->viaCall : ({ a, b }?: { a?: number | undefined; b?: unknown; }) => void ->a : number | undefined +>viaCall : ({ a, b }?: { a?: number | undefined; b?: any; }) => void +>a : any >1 as number | undefined : number | undefined >1 : 1 ->b : unknown ->id(a) : unknown +>b : any +>id(a) : any >id : (v: T) => T ->a : number | undefined +>a : any >{} : {} const n: string = b; >n : string ->b : unknown +>b : any } function viaCallArray({ a = 1 as number | undefined, b = id([a]) } = {}) { >viaCallArray : ({ a, b }?: { a?: number | undefined; b?: any[] | undefined; }) => void ->a : number | undefined +>a : any >1 as number | undefined : number | undefined >1 : 1 >b : any[] >id([a]) : any[] >id : (v: T) => T ->[a] : (number | undefined)[] ->a : number | undefined +>[a] : any[] +>a : any >{} : {} const n: string = b; diff --git a/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=default).errors.txt index e8f947ec283b1..e208e03814c1f 100644 --- a/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=default).errors.txt @@ -1,13 +1,14 @@ relationComplexityError.ts(10,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. relationComplexityError.ts(10,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. relationComplexityError.ts(18,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +relationComplexityError.ts(18,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +relationComplexityError.ts(22,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. relationComplexityError.ts(22,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. -relationComplexityError.ts(26,5): error TS2322: Type '{ p: T1 & T2; }' is not assignable to type '{ p: T1 | null; }'. - Types of property 'p' are incompatible. - Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +relationComplexityError.ts(26,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. +relationComplexityError.ts(26,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. -==== relationComplexityError.ts (5 errors) ==== +==== relationComplexityError.ts (8 errors) ==== // Repro from #55630 // Every comparison that needs a comparison that is too complex is too complex // itself, whether or not that comparison happened before. @@ -31,20 +32,24 @@ relationComplexityError.ts(26,5): error TS2322: Type '{ p: T1 & T2; }' is not as function f2(x: T1 | null, y: T1 & T2) { x = y; // Complexity error ~ +!!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. + ~~~~~ !!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. } function f3(x: T1 | null, y: T1 & T2) { x = y; // Complexity error ~ +!!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. + ~~~~~ !!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. } function f4(x: { p: T1 | null }, y: { p: T1 & T2 }) { x = y; // Complexity error ~ -!!! error TS2322: Type '{ p: T1 & T2; }' is not assignable to type '{ p: T1 | null; }'. -!!! error TS2322: Types of property 'p' are incompatible. -!!! error TS2322: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +!!! error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. + ~~~~~ +!!! error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=typesfirst).errors.txt index 8a92179def8a5..e208e03814c1f 100644 --- a/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/relationComplexityError(checkorder=typesfirst).errors.txt @@ -1,13 +1,14 @@ -relationComplexityError.ts(10,5): error TS2322: Type '{ p: T1 & T2; }' is not assignable to type '{ p: T1 | null; }'. - Types of property 'p' are incompatible. - Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +relationComplexityError.ts(10,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. +relationComplexityError.ts(10,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. relationComplexityError.ts(18,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +relationComplexityError.ts(18,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +relationComplexityError.ts(22,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. relationComplexityError.ts(22,5): error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. relationComplexityError.ts(26,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. relationComplexityError.ts(26,5): error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. -==== relationComplexityError.ts (5 errors) ==== +==== relationComplexityError.ts (8 errors) ==== // Repro from #55630 // Every comparison that needs a comparison that is too complex is too complex // itself, whether or not that comparison happened before. @@ -19,9 +20,9 @@ relationComplexityError.ts(26,5): error TS2859: Excessive complexity comparing t function f0(x: { p: T1 | null }, y: { p: T1 & T2 }) { x = y; // Complexity error ~ -!!! error TS2322: Type '{ p: T1 & T2; }' is not assignable to type '{ p: T1 | null; }'. -!!! error TS2322: Types of property 'p' are incompatible. -!!! error TS2322: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. +!!! error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. + ~~~~~ +!!! error TS2859: Excessive complexity comparing types '{ p: T1 & T2; }' and '{ p: T1 | null; }'. } function f1(x: T1, y: T1 & T2) { @@ -31,12 +32,16 @@ relationComplexityError.ts(26,5): error TS2859: Excessive complexity comparing t function f2(x: T1 | null, y: T1 & T2) { x = y; // Complexity error ~ +!!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. + ~~~~~ !!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. } function f3(x: T1 | null, y: T1 & T2) { x = y; // Complexity error ~ +!!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. + ~~~~~ !!! error TS2859: Excessive complexity comparing types 'T1 & T2' and 'T1 | null'. } diff --git a/tsc/testdata/baselines/reference/compiler/relationDeeplyNestedCheckOrder.errors.txt b/tsc/testdata/baselines/reference/compiler/relationDeeplyNestedCheckOrder.errors.txt deleted file mode 100644 index b12e3d7fff298..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/relationDeeplyNestedCheckOrder.errors.txt +++ /dev/null @@ -1,32 +0,0 @@ -relationDeeplyNestedCheckOrder.ts(13,1): error TS2322: Type 'A<{ a: string; }>' is not assignable to type 'A<{ a: "lit"; }>'. - The types of 'm1.a.m0.m1' are incompatible between these types. - Type 'string' is not assignable to type '"lit"'. -relationDeeplyNestedCheckOrder.ts(14,1): error TS2322: Type 'B<{ a: string; }>' is not assignable to type 'B<{ a: "lit"; }>'. - The types of 'm0.m1.a.m0.m1' are incompatible between these types. - Type 'string' is not assignable to type '"lit"'. - - -==== relationDeeplyNestedCheckOrder.ts (2 errors) ==== - // Relating 'B<{ a: string }>' to 'B<{ a: "lit" }>' eventually relates 'string' - // to '"lit"' through 'm0.m1.a.m0.m1', but only after measuring the variances - // of 'A', which gives up on deeply nested types. TypeScript reports an error - // for the 'B' assignment only when it is checked before the 'A' assignment; - // the result must not depend on which is checked first. - - type A = { m0: (x: B) => void; m1: { [K in keyof T]: B } }; - type B = { m0: A }; - - declare let a1: A<{ a: string }>, a2: A<{ a: "lit" }>; - declare let b1: B<{ a: string }>, b2: B<{ a: "lit" }>; - - a2 = a1; - ~~ -!!! error TS2322: Type 'A<{ a: string; }>' is not assignable to type 'A<{ a: "lit"; }>'. -!!! error TS2322: The types of 'm1.a.m0.m1' are incompatible between these types. -!!! error TS2322: Type 'string' is not assignable to type '"lit"'. - b2 = b1; - ~~ -!!! error TS2322: Type 'B<{ a: string; }>' is not assignable to type 'B<{ a: "lit"; }>'. -!!! error TS2322: The types of 'm0.m1.a.m0.m1' are incompatible between these types. -!!! error TS2322: Type 'string' is not assignable to type '"lit"'. - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=default).errors.txt index 2c10283c820ef..afb511fd91c39 100644 --- a/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=default).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=default).errors.txt @@ -1,9 +1,18 @@ +templateLiteralTypeExcessiveLength.ts(14,16): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(15,6): error TS2589: Type instantiation is excessively deep and possibly infinite. templateLiteralTypeExcessiveLength.ts(15,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(15,28): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(19,21): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(20,6): error TS2589: Type instantiation is excessively deep and possibly infinite. templateLiteralTypeExcessiveLength.ts(20,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(20,31): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(33,24): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(34,6): error TS2589: Type instantiation is excessively deep and possibly infinite. templateLiteralTypeExcessiveLength.ts(34,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(34,38): error TS2589: Type instantiation is excessively deep and possibly infinite. -==== templateLiteralTypeExcessiveLength.ts (3 errors) ==== +==== templateLiteralTypeExcessiveLength.ts (12 errors) ==== // Regression test for #63271: `Dec<2>` is `any`, so `Recur` produces both conditional branches and // keeps tail-recursing forever. The checker must report an error instead of building an unbounded // template literal type. @@ -18,17 +27,33 @@ templateLiteralTypeExcessiveLength.ts(34,11): error TS2589: Type instantiation i // The string literal doubles on every iteration. type Explode = { + ~ [P in Recur<5, "a"> as `${P}_key`]: any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. }; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. // The number of placeholders doubles on every iteration. type ExplodeSpans = { + ~ [P in Recur<5, string> as `${P}_key`]: any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. }; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. // Every text segment stays small, but the number of segments doubles on every iteration, // so the combined text length grows without bound. @@ -41,8 +66,16 @@ templateLiteralTypeExcessiveLength.ts(34,11): error TS2589: Type instantiation i N extends 0 ? S : RecurSegments, `${S}${string}${S}`>; type ExplodeSegments = { + ~ [P in RecurSegments<5, A1024> as `${P}_key`]: any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~ !!! error TS2589: Type instantiation is excessively deep and possibly infinite. }; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=typesfirst).errors.txt index a7a577d5080f1..afb511fd91c39 100644 --- a/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/templateLiteralTypeExcessiveLength(checkorder=typesfirst).errors.txt @@ -1,8 +1,18 @@ -error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(14,16): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(15,6): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(15,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(15,28): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(19,21): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(20,6): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(20,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(20,31): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(33,24): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(34,6): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(34,11): error TS2589: Type instantiation is excessively deep and possibly infinite. +templateLiteralTypeExcessiveLength.ts(34,38): error TS2589: Type instantiation is excessively deep and possibly infinite. -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. -==== templateLiteralTypeExcessiveLength.ts (0 errors) ==== +==== templateLiteralTypeExcessiveLength.ts (12 errors) ==== // Regression test for #63271: `Dec<2>` is `any`, so `Recur` produces both conditional branches and // keeps tail-recursing forever. The checker must report an error instead of building an unbounded // template literal type. @@ -17,13 +27,33 @@ error TS2589: Type instantiation is excessively deep and possibly infinite. // The string literal doubles on every iteration. type Explode = { + ~ [P in Recur<5, "a"> as `${P}_key`]: any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. }; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. // The number of placeholders doubles on every iteration. type ExplodeSpans = { + ~ [P in Recur<5, string> as `${P}_key`]: any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. }; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. // Every text segment stays small, but the number of segments doubles on every iteration, // so the combined text length grows without bound. @@ -36,6 +66,16 @@ error TS2589: Type instantiation is excessively deep and possibly infinite. N extends 0 ? S : RecurSegments, `${S}${string}${S}`>; type ExplodeSegments = { + ~ [P in RecurSegments<5, A1024> as `${P}_key`]: any; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + ~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. }; + ~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/typeParameterHasSelfAsConstraint(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/typeParameterHasSelfAsConstraint(checkorder=typesfirst).errors.txt index 83997ce99f298..b7a0b982aceb6 100644 --- a/tsc/testdata/baselines/reference/compiler/typeParameterHasSelfAsConstraint(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/typeParameterHasSelfAsConstraint(checkorder=typesfirst).errors.txt @@ -6,7 +6,6 @@ typeParameterHasSelfAsConstraint.ts(2,5): error TS2322: Type 'T' is not assignab function foo(x: T): number { ~ !!! error TS2313: Type parameter 'T' has a circular constraint. -!!! related TS2751 typeParameterHasSelfAsConstraint.ts:2:12: Circularity originates in type at this location. return x; ~~~~~~ !!! error TS2322: Type 'T' is not assignable to type 'number'. diff --git a/tsc/testdata/baselines/reference/compiler/typeParameterWithInvalidConstraintType.errors.txt b/tsc/testdata/baselines/reference/compiler/typeParameterWithInvalidConstraintType.errors.txt index 7ee5828671802..342cb732ba6ff 100644 --- a/tsc/testdata/baselines/reference/compiler/typeParameterWithInvalidConstraintType.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/typeParameterWithInvalidConstraintType.errors.txt @@ -10,7 +10,6 @@ typeParameterWithInvalidConstraintType.ts(7,17): error TS2349: This expression i class A { ~ !!! error TS2313: Type parameter 'T' has a circular constraint. -!!! related TS2751 typeParameterWithInvalidConstraintType.ts:4:17: Circularity originates in type at this location. foo() { var x!: T; var a = x.foo(); diff --git a/tsc/testdata/baselines/reference/compiler/unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1(checkorder=typesfirst).errors.txt index 387fe2f14d0be..f2bff262ffc44 100644 --- a/tsc/testdata/baselines/reference/compiler/unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1(checkorder=typesfirst).errors.txt @@ -1,14 +1,17 @@ unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts(1,18): error TS6133: 'person' is declared but its value is never read. +unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts(1,34): error TS6133: 'person2' is declared but its value is never read. unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts(2,9): error TS6133: 'unused' is declared but its value is never read. unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts(3,14): error TS6133: 'maker' is declared but its value is never read. unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts(3,20): error TS6133: 'child' is declared but its value is never read. unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts(4,13): error TS6133: 'unused2' is declared but its value is never read. -==== unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts (5 errors) ==== +==== unusedLocalsOnFunctionDeclarationWithinFunctionDeclaration1.ts (6 errors) ==== function greeter(person: string, person2: string) { ~~~~~~ !!! error TS6133: 'person' is declared but its value is never read. + ~~~~~~~ +!!! error TS6133: 'person2' is declared but its value is never read. var unused = 20; ~~~~~~ !!! error TS6133: 'unused' is declared but its value is never read. diff --git a/tsc/testdata/baselines/reference/compiler/useBeforeDeclaration_classDecorators.2(experimentaldecorators=false).errors.txt b/tsc/testdata/baselines/reference/compiler/useBeforeDeclaration_classDecorators.2(experimentaldecorators=false).errors.txt index 38d1631f0deab..3ccd49b9642ed 100644 --- a/tsc/testdata/baselines/reference/compiler/useBeforeDeclaration_classDecorators.2(experimentaldecorators=false).errors.txt +++ b/tsc/testdata/baselines/reference/compiler/useBeforeDeclaration_classDecorators.2(experimentaldecorators=false).errors.txt @@ -14,8 +14,11 @@ useBeforeDeclaration_classDecorators.2.ts(41,10): error TS2449: Class 'C5' used useBeforeDeclaration_classDecorators.2.ts(42,10): error TS2449: Class 'C5' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(43,10): error TS2449: Class 'C5' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(46,17): error TS1206: Decorators are not valid here. +useBeforeDeclaration_classDecorators.2.ts(46,22): error TS2449: Class 'C5' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(47,20): error TS1206: Decorators are not valid here. +useBeforeDeclaration_classDecorators.2.ts(47,25): error TS2449: Class 'C5' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(48,13): error TS1206: Decorators are not valid here. +useBeforeDeclaration_classDecorators.2.ts(48,18): error TS2449: Class 'C5' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(53,17): error TS2449: Class 'C6' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(54,17): error TS2449: Class 'C6' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(55,17): error TS2449: Class 'C6' used before its declaration. @@ -27,11 +30,14 @@ useBeforeDeclaration_classDecorators.2.ts(61,17): error TS2449: Class 'C6' used useBeforeDeclaration_classDecorators.2.ts(62,17): error TS2449: Class 'C6' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(63,17): error TS2449: Class 'C6' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(66,17): error TS1206: Decorators are not valid here. +useBeforeDeclaration_classDecorators.2.ts(66,29): error TS2449: Class 'C6' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(67,20): error TS1206: Decorators are not valid here. +useBeforeDeclaration_classDecorators.2.ts(67,32): error TS2449: Class 'C6' used before its declaration. useBeforeDeclaration_classDecorators.2.ts(68,13): error TS1206: Decorators are not valid here. +useBeforeDeclaration_classDecorators.2.ts(68,25): error TS2449: Class 'C6' used before its declaration. -==== useBeforeDeclaration_classDecorators.2.ts (31 errors) ==== +==== useBeforeDeclaration_classDecorators.2.ts (37 errors) ==== declare const dec: any; // ok @@ -122,12 +128,21 @@ useBeforeDeclaration_classDecorators.2.ts(68,13): error TS1206: Decorators are n constructor(@dec(C5) a: any) {} ~ !!! error TS1206: Decorators are not valid here. + ~~ +!!! error TS2449: Class 'C5' used before its declaration. +!!! related TS2728 useBeforeDeclaration_classDecorators.2.ts:32:7: 'C5' is declared here. static method2(@dec(C5) a: any) {} ~ !!! error TS1206: Decorators are not valid here. + ~~ +!!! error TS2449: Class 'C5' used before its declaration. +!!! related TS2728 useBeforeDeclaration_classDecorators.2.ts:32:7: 'C5' is declared here. method2(@dec(C5) a: any) {} ~ !!! error TS1206: Decorators are not valid here. + ~~ +!!! error TS2449: Class 'C5' used before its declaration. +!!! related TS2728 useBeforeDeclaration_classDecorators.2.ts:32:7: 'C5' is declared here. } // error @@ -178,11 +193,20 @@ useBeforeDeclaration_classDecorators.2.ts(68,13): error TS1206: Decorators are n constructor(@dec((() => C6)()) a: any) {} ~ !!! error TS1206: Decorators are not valid here. + ~~ +!!! error TS2449: Class 'C6' used before its declaration. +!!! related TS2728 useBeforeDeclaration_classDecorators.2.ts:52:7: 'C6' is declared here. static method2(@dec((() => C6)()) a: any) {} ~ !!! error TS1206: Decorators are not valid here. + ~~ +!!! error TS2449: Class 'C6' used before its declaration. +!!! related TS2728 useBeforeDeclaration_classDecorators.2.ts:52:7: 'C6' is declared here. method2(@dec((() => C6)()) a: any) {} ~ !!! error TS1206: Decorators are not valid here. + ~~ +!!! error TS2449: Class 'C6' used before its declaration. +!!! related TS2728 useBeforeDeclaration_classDecorators.2.ts:52:7: 'C6' is declared here. } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/varianceCircularConsistency.errors.txt b/tsc/testdata/baselines/reference/compiler/varianceCircularConsistency.errors.txt index a4725d41bf2bf..060c23148616c 100644 --- a/tsc/testdata/baselines/reference/compiler/varianceCircularConsistency.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/varianceCircularConsistency.errors.txt @@ -1,14 +1,22 @@ +varianceCircularConsistency.ts(25,7): error TS2322: Type 'EntryPoint void; }, {}>, { slug: string; }>' is not assignable to type 'EntryPoint'. + Types of property 'root' are incompatible. + Type 'Ref>' is not assignable to type 'Ref, never, never, {}>>'. + Type 'EntryPointComponent<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>' is not assignable to type 'EntryPointComponent, never, never, {}>'. + Type '(props: Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>) => void' is not assignable to type 'EntryPointComponent, never, never, {}>'. + Type '(props: Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>) => void' is not assignable to type '(props: Props, never, never, {}>) => void'. + Types of parameters 'props' and 'props' are incompatible. + Type 'Props, never, never, {}>' is not assignable to type 'Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>'. + Property 'q' is missing in type 'Record' but required in type '{ q: { readonly variables: { slug: string; }; }; }'. varianceCircularConsistency.ts(26,7): error TS2322: Type 'Ref>' is not assignable to type 'Ref, never, never, {}>>'. Type 'EntryPointComponent<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>' is not assignable to type 'EntryPointComponent, never, never, {}>'. Type '(props: Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>) => void' is not assignable to type 'EntryPointComponent, never, never, {}>'. Type '(props: Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>) => void' is not assignable to type '(props: Props, never, never, {}>) => void'. Types of parameters 'props' and 'props' are incompatible. Type 'Props, never, never, {}>' is not assignable to type 'Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>'. - Types of property 'queries' are incompatible. - Property 'q' is missing in type 'Record' but required in type '{ q: { readonly variables: { slug: string; }; }; }'. + Property 'q' is missing in type 'Record' but required in type '{ q: { readonly variables: { slug: string; }; }; }'. -==== varianceCircularConsistency.ts (1 errors) ==== +==== varianceCircularConsistency.ts (2 errors) ==== // Variances of these mutually recursive generics must not depend on values // measured mid-cycle. Assigning `e` to `EntryPoint` must agree with // assigning `e.root` to `EntryPoint["root"]`. @@ -34,6 +42,17 @@ varianceCircularConsistency.ts(26,7): error TS2322: Type 'Ref void }>; declare const e: EntryPoint; const whole: EntryPoint = e; + ~~~~~ +!!! error TS2322: Type 'EntryPoint void; }, {}>, { slug: string; }>' is not assignable to type 'EntryPoint'. +!!! error TS2322: Types of property 'root' are incompatible. +!!! error TS2322: Type 'Ref>' is not assignable to type 'Ref, never, never, {}>>'. +!!! error TS2322: Type 'EntryPointComponent<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>' is not assignable to type 'EntryPointComponent, never, never, {}>'. +!!! error TS2322: Type '(props: Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>) => void' is not assignable to type 'EntryPointComponent, never, never, {}>'. +!!! error TS2322: Type '(props: Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>) => void' is not assignable to type '(props: Props, never, never, {}>) => void'. +!!! error TS2322: Types of parameters 'props' and 'props' are incompatible. +!!! error TS2322: Type 'Props, never, never, {}>' is not assignable to type 'Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>'. +!!! error TS2322: Property 'q' is missing in type 'Record' but required in type '{ q: { readonly variables: { slug: string; }; }; }'. +!!! related TS2728 varianceCircularConsistency.ts:23:40: 'q' is declared here. const root: EntryPoint["root"] = e.root; ~~~~ !!! error TS2322: Type 'Ref>' is not assignable to type 'Ref, never, never, {}>>'. @@ -42,7 +61,6 @@ varianceCircularConsistency.ts(26,7): error TS2322: Type 'Ref) => void' is not assignable to type '(props: Props, never, never, {}>) => void'. !!! error TS2322: Types of parameters 'props' and 'props' are incompatible. !!! error TS2322: Type 'Props, never, never, {}>' is not assignable to type 'Props<{ q: { readonly variables: { slug: string; }; }; }, never, never, {}>'. -!!! error TS2322: Types of property 'queries' are incompatible. -!!! error TS2322: Property 'q' is missing in type 'Record' but required in type '{ q: { readonly variables: { slug: string; }; }; }'. +!!! error TS2322: Property 'q' is missing in type 'Record' but required in type '{ q: { readonly variables: { slug: string; }; }; }'. !!! related TS2728 varianceCircularConsistency.ts:23:40: 'q' is declared here. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/varianceCircularityChartjsPlugin(checkorder=default).errors.txt b/tsc/testdata/baselines/reference/compiler/varianceCircularityChartjsPlugin(checkorder=default).errors.txt new file mode 100644 index 0000000000000..f34bc94da7648 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/varianceCircularityChartjsPlugin(checkorder=default).errors.txt @@ -0,0 +1,141 @@ +varianceCircularityChartjsPlugin.ts(69,37): error TS2322: Type '(chart: Chart) => void' is not assignable to type '(chart: Chart<"bar", "bar">) => void'. + Types of parameters 'chart' and 'chart' are incompatible. + Type 'Chart<"bar", "bar">' is not assignable to type 'Chart'. + The types of 'config.options' are incompatible between these types. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. + Types of property 'backgroundColor' are incompatible. + Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. + Type 'Scriptable>' is not assignable to type 'Scriptable>'. + Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. + Types of property 'dataset' are incompatible. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">>'. + Types of property 'tooltip' are incompatible. + Type '_DeepPartialObject> | undefined' is not assignable to type '_DeepPartialObject> | undefined'. + Type '_DeepPartialObject>' is not assignable to type '_DeepPartialObject>'. + Types of property 'callbacks' are incompatible. + Type 'TooltipDatasetCallbacks> | undefined' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">> | undefined'. + Type 'TooltipDatasetCallbacks>' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">>'. + Type 'TooltipModel<"bar">' is not assignable to type 'TooltipModel'. + The types of 'chart.config.options' are incompatible between these types. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. + Types of property 'backgroundColor' are incompatible. + Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. + Type 'Scriptable>' is not assignable to type 'Scriptable>'. + Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. + Types of property 'dataset' are incompatible. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type 'ChartDatasetProperties<"bar", "bar">'. + Types of property 'data' are incompatible. + Type 'keyof ChartTypeRegistry' is not assignable to type '"bar"'. + Type '"line"' is not assignable to type '"bar"'. + + +==== varianceCircularityChartjsPlugin.ts (1 errors) ==== + // Reduced from chart.js typings (PeerTube, Unleash). Chart, TooltipModel and + // ScriptableContext form a variance circularity. ScriptableContext is in + // fact invariant: ScriptableContext<"bar" | "line"> is not assignable to + // ScriptableContext<"bar"> ('dataset.data' differs), so Chart<"bar"> is not + // assignable to Chart and the assignment below must be an error. + // Measuring variances from the circularity's entry, main recorded + // ScriptableContext/TooltipModel as bivariant (measured mid-cycle) and + // accepted it; main reports this error too when ScriptableContext's variance + // is measured first (e.g. after checking a ScriptableContext assignment). + + declare class Element { options: O } + type DeepPartial = T extends Function ? T : T extends object ? _DeepPartialObject : undefined + type _DeepPartialObject = { [P in keyof T]?: DeepPartial } + type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never + type AnyObject = Record + interface ScriptableContext { + dataset: UnionToIntersection> + } + type Scriptable = (ctx: TContext) => T + declare class Chart { + config: ChartConfigurationCustomTypesPerDataset + } + interface Plugin { + id: string; + beforeDraw(chart: Chart): void + } + interface CoreChartOptions { + backgroundColor: Scriptable> + } + interface AnimationSpec { + delay: Scriptable> + } + interface TooltipModel extends Element> { + chart: Chart + } + interface TooltipDatasetCallbacks> { + (this: Model): void + } + interface TooltipOptions { + animation: AnimationSpec + } + interface TooltipDatasetOptions { + callbacks: TooltipDatasetCallbacks + } + interface PluginDatasetOptionsByType { + tooltip: TooltipDatasetOptions + } + interface ChartTypeRegistry { + bar: { defaultDataPoint: [] } + line: { defaultDataPoint: null } + } + type ChartType = keyof ChartTypeRegistry + type DatasetChartOptions = { [key in TType]: {} } + type ChartOptions = Exclude & DatasetChartOptions & TType>, undefined> + interface ChartDatasetProperties { + data: TData + } + type ChartDataset = + DeepPartial> & ChartDatasetProperties + type ChartDatasetCustomTypesPerDataset = DeepPartial> + interface ChartDataCustomTypesPerDataset { + datasets: ChartDatasetCustomTypesPerDataset + } + export interface ChartConfigurationCustomTypesPerDataset { + data: ChartDataCustomTypesPerDataset + options: ChartOptions + } + + const p: Plugin<"bar"> = { id: "x", beforeDraw: (chart: Chart) => {} }; + ~~~~~~~~~~ +!!! error TS2322: Type '(chart: Chart) => void' is not assignable to type '(chart: Chart<"bar", "bar">) => void'. +!!! error TS2322: Types of parameters 'chart' and 'chart' are incompatible. +!!! error TS2322: Type 'Chart<"bar", "bar">' is not assignable to type 'Chart'. +!!! error TS2322: The types of 'config.options' are incompatible between these types. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Types of property 'backgroundColor' are incompatible. +!!! error TS2322: Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. +!!! error TS2322: Type 'Scriptable>' is not assignable to type 'Scriptable>'. +!!! error TS2322: Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. +!!! error TS2322: Types of property 'dataset' are incompatible. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">>'. +!!! error TS2322: Types of property 'tooltip' are incompatible. +!!! error TS2322: Type '_DeepPartialObject> | undefined' is not assignable to type '_DeepPartialObject> | undefined'. +!!! error TS2322: Type '_DeepPartialObject>' is not assignable to type '_DeepPartialObject>'. +!!! error TS2322: Types of property 'callbacks' are incompatible. +!!! error TS2322: Type 'TooltipDatasetCallbacks> | undefined' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">> | undefined'. +!!! error TS2322: Type 'TooltipDatasetCallbacks>' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">>'. +!!! error TS2322: Type 'TooltipModel<"bar">' is not assignable to type 'TooltipModel'. +!!! error TS2322: The types of 'chart.config.options' are incompatible between these types. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Types of property 'backgroundColor' are incompatible. +!!! error TS2322: Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. +!!! error TS2322: Type 'Scriptable>' is not assignable to type 'Scriptable>'. +!!! error TS2322: Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. +!!! error TS2322: Types of property 'dataset' are incompatible. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type 'ChartDatasetProperties<"bar", "bar">'. +!!! error TS2322: Types of property 'data' are incompatible. +!!! error TS2322: Type 'keyof ChartTypeRegistry' is not assignable to type '"bar"'. +!!! error TS2322: Type '"line"' is not assignable to type '"bar"'. +!!! related TS6500 varianceCircularityChartjsPlugin.ts:25:5: The expected type comes from property 'beforeDraw' which is declared here on type 'Plugin<"bar">' + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/varianceCircularityChartjsPlugin(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/compiler/varianceCircularityChartjsPlugin(checkorder=typesfirst).errors.txt new file mode 100644 index 0000000000000..f34bc94da7648 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/varianceCircularityChartjsPlugin(checkorder=typesfirst).errors.txt @@ -0,0 +1,141 @@ +varianceCircularityChartjsPlugin.ts(69,37): error TS2322: Type '(chart: Chart) => void' is not assignable to type '(chart: Chart<"bar", "bar">) => void'. + Types of parameters 'chart' and 'chart' are incompatible. + Type 'Chart<"bar", "bar">' is not assignable to type 'Chart'. + The types of 'config.options' are incompatible between these types. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. + Types of property 'backgroundColor' are incompatible. + Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. + Type 'Scriptable>' is not assignable to type 'Scriptable>'. + Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. + Types of property 'dataset' are incompatible. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">>'. + Types of property 'tooltip' are incompatible. + Type '_DeepPartialObject> | undefined' is not assignable to type '_DeepPartialObject> | undefined'. + Type '_DeepPartialObject>' is not assignable to type '_DeepPartialObject>'. + Types of property 'callbacks' are incompatible. + Type 'TooltipDatasetCallbacks> | undefined' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">> | undefined'. + Type 'TooltipDatasetCallbacks>' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">>'. + Type 'TooltipModel<"bar">' is not assignable to type 'TooltipModel'. + The types of 'chart.config.options' are incompatible between these types. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. + Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. + Types of property 'backgroundColor' are incompatible. + Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. + Type 'Scriptable>' is not assignable to type 'Scriptable>'. + Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. + Types of property 'dataset' are incompatible. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. + Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type 'ChartDatasetProperties<"bar", "bar">'. + Types of property 'data' are incompatible. + Type 'keyof ChartTypeRegistry' is not assignable to type '"bar"'. + Type '"line"' is not assignable to type '"bar"'. + + +==== varianceCircularityChartjsPlugin.ts (1 errors) ==== + // Reduced from chart.js typings (PeerTube, Unleash). Chart, TooltipModel and + // ScriptableContext form a variance circularity. ScriptableContext is in + // fact invariant: ScriptableContext<"bar" | "line"> is not assignable to + // ScriptableContext<"bar"> ('dataset.data' differs), so Chart<"bar"> is not + // assignable to Chart and the assignment below must be an error. + // Measuring variances from the circularity's entry, main recorded + // ScriptableContext/TooltipModel as bivariant (measured mid-cycle) and + // accepted it; main reports this error too when ScriptableContext's variance + // is measured first (e.g. after checking a ScriptableContext assignment). + + declare class Element { options: O } + type DeepPartial = T extends Function ? T : T extends object ? _DeepPartialObject : undefined + type _DeepPartialObject = { [P in keyof T]?: DeepPartial } + type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never + type AnyObject = Record + interface ScriptableContext { + dataset: UnionToIntersection> + } + type Scriptable = (ctx: TContext) => T + declare class Chart { + config: ChartConfigurationCustomTypesPerDataset + } + interface Plugin { + id: string; + beforeDraw(chart: Chart): void + } + interface CoreChartOptions { + backgroundColor: Scriptable> + } + interface AnimationSpec { + delay: Scriptable> + } + interface TooltipModel extends Element> { + chart: Chart + } + interface TooltipDatasetCallbacks> { + (this: Model): void + } + interface TooltipOptions { + animation: AnimationSpec + } + interface TooltipDatasetOptions { + callbacks: TooltipDatasetCallbacks + } + interface PluginDatasetOptionsByType { + tooltip: TooltipDatasetOptions + } + interface ChartTypeRegistry { + bar: { defaultDataPoint: [] } + line: { defaultDataPoint: null } + } + type ChartType = keyof ChartTypeRegistry + type DatasetChartOptions = { [key in TType]: {} } + type ChartOptions = Exclude & DatasetChartOptions & TType>, undefined> + interface ChartDatasetProperties { + data: TData + } + type ChartDataset = + DeepPartial> & ChartDatasetProperties + type ChartDatasetCustomTypesPerDataset = DeepPartial> + interface ChartDataCustomTypesPerDataset { + datasets: ChartDatasetCustomTypesPerDataset + } + export interface ChartConfigurationCustomTypesPerDataset { + data: ChartDataCustomTypesPerDataset + options: ChartOptions + } + + const p: Plugin<"bar"> = { id: "x", beforeDraw: (chart: Chart) => {} }; + ~~~~~~~~~~ +!!! error TS2322: Type '(chart: Chart) => void' is not assignable to type '(chart: Chart<"bar", "bar">) => void'. +!!! error TS2322: Types of parameters 'chart' and 'chart' are incompatible. +!!! error TS2322: Type 'Chart<"bar", "bar">' is not assignable to type 'Chart'. +!!! error TS2322: The types of 'config.options' are incompatible between these types. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Types of property 'backgroundColor' are incompatible. +!!! error TS2322: Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. +!!! error TS2322: Type 'Scriptable>' is not assignable to type 'Scriptable>'. +!!! error TS2322: Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. +!!! error TS2322: Types of property 'dataset' are incompatible. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">>'. +!!! error TS2322: Types of property 'tooltip' are incompatible. +!!! error TS2322: Type '_DeepPartialObject> | undefined' is not assignable to type '_DeepPartialObject> | undefined'. +!!! error TS2322: Type '_DeepPartialObject>' is not assignable to type '_DeepPartialObject>'. +!!! error TS2322: Types of property 'callbacks' are incompatible. +!!! error TS2322: Type 'TooltipDatasetCallbacks> | undefined' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">> | undefined'. +!!! error TS2322: Type 'TooltipDatasetCallbacks>' is not assignable to type 'TooltipDatasetCallbacks<"bar", TooltipModel<"bar">>'. +!!! error TS2322: Type 'TooltipModel<"bar">' is not assignable to type 'TooltipModel'. +!!! error TS2322: The types of 'chart.config.options' are incompatible between these types. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "bar"> | _DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Type '_DeepPartialObject & DatasetChartOptions<"bar"> & "bar">' is not assignable to type '_DeepPartialObject & DatasetChartOptions & "line">'. +!!! error TS2322: Types of property 'backgroundColor' are incompatible. +!!! error TS2322: Type 'Scriptable> | undefined' is not assignable to type 'Scriptable> | undefined'. +!!! error TS2322: Type 'Scriptable>' is not assignable to type 'Scriptable>'. +!!! error TS2322: Type 'ScriptableContext' is not assignable to type 'ScriptableContext<"bar">'. +!!! error TS2322: Types of property 'dataset' are incompatible. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType<"bar">> & ChartDatasetProperties<"bar", "bar">'. +!!! error TS2322: Type '_DeepPartialObject<{ defaultDataPoint: []; } & PluginDatasetOptionsByType> & ChartDatasetProperties & _DeepPartialObject<{ defaultDataPoint: null; } & PluginDatasetOptionsByType>' is not assignable to type 'ChartDatasetProperties<"bar", "bar">'. +!!! error TS2322: Types of property 'data' are incompatible. +!!! error TS2322: Type 'keyof ChartTypeRegistry' is not assignable to type '"bar"'. +!!! error TS2322: Type '"line"' is not assignable to type '"bar"'. +!!! related TS6500 varianceCircularityChartjsPlugin.ts:25:5: The expected type comes from property 'beforeDraw' which is declared here on type 'Plugin<"bar">' + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.errors.txt b/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.errors.txt index 570f693fae100..67a74da6b9b6d 100644 --- a/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.errors.txt @@ -1,4 +1,4 @@ -varianceCycleDiscardedDiagnostics.ts(14,5): error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +varianceCycleDiscardedDiagnostics.ts(14,33): error TS2339: Property 'foo' does not exist on type 'A'. ==== varianceCycleDiscardedDiagnostics.ts (1 errors) ==== @@ -16,8 +16,8 @@ varianceCycleDiscardedDiagnostics.ts(14,5): error TS7022: 'Value' implicitly has class Bar { x!: T; Value = pick(new Foo(this)).foo; - ~~~~~ -!!! error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'A'. } class Foo { diff --git a/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.symbols b/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.symbols index 437eb30b0dc81..e08928ce4a02d 100644 --- a/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.symbols +++ b/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.symbols @@ -36,11 +36,9 @@ class Bar { Value = pick(new Foo(this)).foo; >Value : Symbol(Bar.Value, Decl(varianceCycleDiscardedDiagnostics.ts, 12, 10)) ->pick(new Foo(this)).foo : Symbol(B.foo, Decl(varianceCycleDiscardedDiagnostics.ts, 7, 13)) >pick : Symbol(pick, Decl(varianceCycleDiscardedDiagnostics.ts, 7, 27), Decl(varianceCycleDiscardedDiagnostics.ts, 8, 41)) >Foo : Symbol(Foo, Decl(varianceCycleDiscardedDiagnostics.ts, 14, 1)) >this : Symbol(Bar, Decl(varianceCycleDiscardedDiagnostics.ts, 9, 36)) ->foo : Symbol(B.foo, Decl(varianceCycleDiscardedDiagnostics.ts, 7, 13)) } class Foo { diff --git a/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.types b/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.types index 6d56cf12120d4..ad994f6a17985 100644 --- a/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.types +++ b/tsc/testdata/baselines/reference/compiler/varianceCycleDiscardedDiagnostics.types @@ -29,13 +29,13 @@ class Bar { Value = pick(new Foo(this)).foo; >Value : any ->pick(new Foo(this)).foo : string ->pick(new Foo(this)) : B +>pick(new Foo(this)).foo : any +>pick(new Foo(this)) : A >pick : { (x: Foo): A; (x: object): B; } >new Foo(this) : Foo >Foo : typeof Foo >this : this ->foo : string +>foo : any } class Foo { diff --git a/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).errors.txt index 981a83e22c019..5529a3e5e68c6 100644 --- a/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).errors.txt @@ -22,12 +22,11 @@ dependentDestructuredVariablesReferenceInDeclaration1.ts(25,14): error TS2339: P dependentDestructuredVariablesReferenceInDeclaration1.ts(25,17): error TS2339: Property 'f' does not exist on type 'string | number'. dependentDestructuredVariablesReferenceInDeclaration1.ts(30,14): error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. dependentDestructuredVariablesReferenceInDeclaration1.ts(30,71): error TS2448: Block-scoped variable 'f' used before its declaration. -dependentDestructuredVariablesReferenceInDeclaration1.ts(35,14): error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. dependentDestructuredVariablesReferenceInDeclaration1.ts(58,14): error TS2451: Cannot redeclare block-scoped variable 'f'. dependentDestructuredVariablesReferenceInDeclaration1.ts(58,17): error TS2451: Cannot redeclare block-scoped variable 'f'. -==== dependentDestructuredVariablesReferenceInDeclaration1.ts (27 errors) ==== +==== dependentDestructuredVariablesReferenceInDeclaration1.ts (26 errors) ==== // https://github.com/microsoft/TypeScript/issues/62993 { @@ -113,8 +112,6 @@ dependentDestructuredVariablesReferenceInDeclaration1.ts(58,17): error TS2451: C { const { c, f }: { c: 0; f: () => unknown } | { c: 1; f: string } = { - ~ -!!! error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. c: 0, f: () => f, }; diff --git a/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).types b/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).types index fda8b5b052297..1e1c61a26976d 100644 --- a/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).types +++ b/tsc/testdata/baselines/reference/conformance/dependentDestructuredVariablesReferenceInDeclaration1(checkorder=typesfirst).types @@ -103,7 +103,7 @@ { const { c, f }: { c: 0; f: () => unknown } | { c: 1; f: string } = { >c : 0 | 1 ->f : any +>f : string | (() => unknown) >c : 0 >f : () => unknown >c : 1 diff --git a/tsc/testdata/baselines/reference/conformance/importTag13(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/conformance/importTag13(checkorder=typesfirst).errors.txt index de660063858df..3427c0e358148 100644 --- a/tsc/testdata/baselines/reference/conformance/importTag13(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/conformance/importTag13(checkorder=typesfirst).errors.txt @@ -1,16 +1,13 @@ /foo.js(1,15): error TS1005: 'from' expected. /foo.js(1,15): error TS1141: String literal expected. -/foo.js(1,25): error TS2307: Cannot find module 'types' or its corresponding type declarations. -==== /foo.js (3 errors) ==== +==== /foo.js (2 errors) ==== /** @import x = require("types") */ ~ !!! error TS1005: 'from' expected. ~~~~~~~~~~~~~~~~~~ !!! error TS1141: String literal expected. - ~~~~~~~ -!!! error TS2307: Cannot find module 'types' or its corresponding type declarations. ==== /types.ts (0 errors) ==== export interface Foo { diff --git a/tsc/testdata/baselines/reference/conformance/parser519458.errors.txt b/tsc/testdata/baselines/reference/conformance/parser519458.errors.txt index 4534e059f31e9..18f6e68c68b55 100644 --- a/tsc/testdata/baselines/reference/conformance/parser519458.errors.txt +++ b/tsc/testdata/baselines/reference/conformance/parser519458.errors.txt @@ -1,14 +1,11 @@ parser519458.ts(1,15): error TS2503: Cannot find namespace 'module'. -parser519458.ts(1,15): error TS2591: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig. parser519458.ts(1,21): error TS1005: ';' expected. -==== parser519458.ts (3 errors) ==== +==== parser519458.ts (2 errors) ==== import rect = module("rect"); var bar = new rect.Rect(); ~~~~~~ !!! error TS2503: Cannot find namespace 'module'. - ~~~~~~ -!!! error TS2591: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig. ~ !!! error TS1005: ';' expected. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/privateNameNestedClassFieldShadowing(checkorder=typesfirst).errors.txt b/tsc/testdata/baselines/reference/conformance/privateNameNestedClassFieldShadowing(checkorder=typesfirst).errors.txt index d4d3aede55faf..634c2349e1ecb 100644 --- a/tsc/testdata/baselines/reference/conformance/privateNameNestedClassFieldShadowing(checkorder=typesfirst).errors.txt +++ b/tsc/testdata/baselines/reference/conformance/privateNameNestedClassFieldShadowing(checkorder=typesfirst).errors.txt @@ -11,8 +11,8 @@ privateNameNestedClassFieldShadowing.ts(7,31): error TS18014: The property '#x' console.log(x.#x); ~~ !!! error TS18014: The property '#x' cannot be accessed on type 'Base' within this class because it is shadowed by another private identifier with the same spelling. -!!! related TS18018 privateNameNestedClassFieldShadowing.ts:2:5: The declaration of '#x' that you probably intended to use is defined here !!! related TS18017 privateNameNestedClassFieldShadowing.ts:5:13: The shadowing declaration of '#x' is defined here +!!! related TS18018 privateNameNestedClassFieldShadowing.ts:2:5: The declaration of '#x' that you probably intended to use is defined here } testDerived(x: Derived) { console.log(x.#x); diff --git a/tsc/testdata/baselines/reference/conformance/recursiveMappedTypes.errors.txt b/tsc/testdata/baselines/reference/conformance/recursiveMappedTypes.errors.txt index 5d6e45f1ffa2e..9d4e77b1bfb1f 100644 --- a/tsc/testdata/baselines/reference/conformance/recursiveMappedTypes.errors.txt +++ b/tsc/testdata/baselines/reference/conformance/recursiveMappedTypes.errors.txt @@ -4,12 +4,14 @@ recursiveMappedTypes.ts(7,6): error TS2456: Type alias 'Recurse1' circularly ref recursiveMappedTypes.ts(8,11): error TS2313: Type parameter 'K' has a circular constraint. recursiveMappedTypes.ts(11,6): error TS2456: Type alias 'Recurse2' circularly references itself. recursiveMappedTypes.ts(12,11): error TS2313: Type parameter 'K' has a circular constraint. +recursiveMappedTypes.ts(20,14): error TS5114: Instantiations of type 'Circular' appear infinitely circular. recursiveMappedTypes.ts(20,19): error TS5114: Instantiations of type 'Circular' appear infinitely circular. +recursiveMappedTypes.ts(21,10): error TS5114: Instantiations of type 'Circular' appear infinitely circular. recursiveMappedTypes.ts(73,5): error TS2502: '"each"' is referenced directly or indirectly in its own type annotation. -recursiveMappedTypes.ts(79,1): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'. +recursiveMappedTypes.ts(73,13): error TS2615: Type of property '"each"' circularly references itself in mapped type '{ type: "type"; minimum_count: "minimum_count"; maximum_count: "maximum_count"; collapsable?: undefined; each: any; }'. -==== recursiveMappedTypes.ts (9 errors) ==== +==== recursiveMappedTypes.ts (11 errors) ==== // Recursive mapped types simply appear empty type Recurse = { @@ -34,7 +36,6 @@ recursiveMappedTypes.ts(79,1): error TS2615: Type of property '"each"' circularl [K in keyof Recurse1]: Recurse1[K] ~~~~~~~~~~~~~~ !!! error TS2313: Type parameter 'K' has a circular constraint. -!!! related TS2751 recursiveMappedTypes.ts:8:17: Circularity originates in type at this location. } // Repro from #27881 @@ -43,9 +44,13 @@ recursiveMappedTypes.ts(79,1): error TS2615: Type of property '"each"' circularl type tup = [number, number, number, number]; function foo(arg: Circular): tup { + ~~~~~~~~~~~~~~~~~~ +!!! error TS5114: Instantiations of type 'Circular' appear infinitely circular. ~~~~~~~~~~~~~ !!! error TS5114: Instantiations of type 'Circular' appear infinitely circular. return arg; + ~~~ +!!! error TS5114: Instantiations of type 'Circular' appear infinitely circular. } // Repro from #29442 @@ -100,14 +105,14 @@ recursiveMappedTypes.ts(79,1): error TS2615: Type of property '"each"' circularl "each": Child; ~~~~~~ !!! error TS2502: '"each"' is referenced directly or indirectly in its own type annotation. + ~~~~~~~~~~~~~~~~~ +!!! error TS2615: Type of property '"each"' circularly references itself in mapped type '{ type: "type"; minimum_count: "minimum_count"; maximum_count: "maximum_count"; collapsable?: undefined; each: any; }'. } type ListChild = Child declare let x: ListChild; x.type; - ~ -!!! error TS2615: Type of property '"each"' circularly references itself in mapped type '{ [P in keyof ListWidget]: undefined extends ListWidget[P] ? never : P; }'. // Repros from #41790 diff --git a/tsc/testdata/baselines/reference/conformance/templateLiteralTypes1.errors.txt b/tsc/testdata/baselines/reference/conformance/templateLiteralTypes1.errors.txt index ed921a5dbfe66..a77ac80910723 100644 --- a/tsc/testdata/baselines/reference/conformance/templateLiteralTypes1.errors.txt +++ b/tsc/testdata/baselines/reference/conformance/templateLiteralTypes1.errors.txt @@ -6,10 +6,13 @@ templateLiteralTypes1.ts(165,15): error TS1338: 'infer' declarations are only pe templateLiteralTypes1.ts(197,16): error TS2590: Expression produces a union type that is too complex to represent. templateLiteralTypes1.ts(201,16): error TS2590: Expression produces a union type that is too complex to represent. templateLiteralTypes1.ts(205,16): error TS2590: Expression produces a union type that is too complex to represent. +templateLiteralTypes1.ts(250,5): error TS2590: Expression produces a union type that is too complex to represent. templateLiteralTypes1.ts(252,7): error TS2590: Expression produces a union type that is too complex to represent. +templateLiteralTypes1.ts(254,7): error TS2590: Expression produces a union type that is too complex to represent. +templateLiteralTypes1.ts(254,14): error TS2590: Expression produces a union type that is too complex to represent. -==== templateLiteralTypes1.ts (7 errors) ==== +==== templateLiteralTypes1.ts (10 errors) ==== // Template types example from #12754 const createScopedActionType = (scope: S) => (type: T) => `${scope}/${type}` as `${S}/${T}`; @@ -275,10 +278,18 @@ templateLiteralTypes1.ts(252,7): error TS2590: Expression produces a union type export type SpacingShorthand = | `${Spacing} ${Spacing}` + ~~~~~~~~~~~~~~~~~~~~~~~~~ | `${Spacing} ${Spacing} ${Spacing}` + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | `${Spacing} ${Spacing} ${Spacing} ${Spacing}`; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2590: Expression produces a union type that is too complex to represent. const test1: SpacingShorthand = "0 0 0"; + ~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. + ~~~~~~~~~~~~~~~~ +!!! error TS2590: Expression produces a union type that is too complex to represent. \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/thisTypeInFunctionsNegative.errors.txt b/tsc/testdata/baselines/reference/conformance/thisTypeInFunctionsNegative.errors.txt index c0edb81a0dde9..f3f7dedc9700c 100644 --- a/tsc/testdata/baselines/reference/conformance/thisTypeInFunctionsNegative.errors.txt +++ b/tsc/testdata/baselines/reference/conformance/thisTypeInFunctionsNegative.errors.txt @@ -79,6 +79,7 @@ thisTypeInFunctionsNegative.ts(169,23): error TS1359: Identifier expected. 'this thisTypeInFunctionsNegative.ts(169,23): error TS2680: A 'this' parameter must be the first parameter. thisTypeInFunctionsNegative.ts(170,23): error TS1005: ',' expected. thisTypeInFunctionsNegative.ts(171,20): error TS1433: Neither decorators nor modifiers may be applied to 'this' parameters. +thisTypeInFunctionsNegative.ts(171,21): error TS2304: Cannot find name 'deco'. thisTypeInFunctionsNegative.ts(172,30): error TS1005: ',' expected. thisTypeInFunctionsNegative.ts(172,32): error TS1359: Identifier expected. 'new' is a reserved word that cannot be used here. thisTypeInFunctionsNegative.ts(172,37): error TS1005: ',' expected. @@ -93,7 +94,7 @@ thisTypeInFunctionsNegative.ts(177,19): error TS2730: An arrow function cannot h thisTypeInFunctionsNegative.ts(178,22): error TS2730: An arrow function cannot have a 'this' parameter. -==== thisTypeInFunctionsNegative.ts (63 errors) ==== +==== thisTypeInFunctionsNegative.ts (64 errors) ==== class C { n: number; explicitThis(this: this, m: number): number { @@ -407,6 +408,8 @@ thisTypeInFunctionsNegative.ts(178,22): error TS2730: An arrow function cannot h function decorated(@deco() this: C): number { return this.n; } ~~~~~~~ !!! error TS1433: Neither decorators nor modifiers may be applied to 'this' parameters. + ~~~~ +!!! error TS2304: Cannot find name 'deco'. function initializer(this: C = new C()): number { return this.n; } ~ !!! error TS1005: ',' expected. diff --git a/tsc/testdata/baselines/reference/tsc/incremental/global-diagnostics-produced-during-semantic-checking.js b/tsc/testdata/baselines/reference/tsc/incremental/global-diagnostics-produced-during-semantic-checking.js index 3c629aeec7fcb..98bd4a0d2e0e1 100644 --- a/tsc/testdata/baselines/reference/tsc/incremental/global-diagnostics-produced-during-semantic-checking.js +++ b/tsc/testdata/baselines/reference/tsc/incremental/global-diagnostics-produced-during-semantic-checking.js @@ -191,10 +191,14 @@ export function* values() { yield 1; } tsgo -ExitStatus:: Success +ExitStatus:: DiagnosticsPresent_OutputsGenerated Output:: +error TS2318: Cannot find global type 'IterableIterator'. + +Found 1 error. + //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","errors":true,"root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"303d5119b8941b887ef975780b83caec-export function* values() { yield 1; }\n// comment-only edit\n","signature":"b9f7a0d32d9887fdd864bf1c0dcd3591-export declare function values(): {};\n","impliedNodeFormat":1}],"affectedFilesPendingEmit":[2]} +{"version":"FakeTSVersion","errors":true,"root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"303d5119b8941b887ef975780b83caec-export function* values() { yield 1; }\n// comment-only edit\n","signature":"b9f7a0d32d9887fdd864bf1c0dcd3591-export declare function values(): {};\n","impliedNodeFormat":1}],"semanticDiagnosticsPerFile":[[2,[{"noFile":true,"code":2318,"category":1,"messageKey":"Cannot_find_global_type_0_2318","messageArgs":["IterableIterator"]}]]],"affectedFilesPendingEmit":[2]} //// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* { "version": "FakeTSVersion", @@ -236,6 +240,22 @@ Output:: } } ], + "semanticDiagnosticsPerFile": [ + [ + "./repro.ts", + [ + { + "noFile": true, + "code": 2318, + "category": 1, + "messageKey": "Cannot_find_global_type_0_2318", + "messageArgs": [ + "IterableIterator" + ] + } + ] + ] + ], "affectedFilesPendingEmit": [ [ "./repro.ts", @@ -243,7 +263,7 @@ Output:: 2 ] ], - "size": 1136 + "size": 1295 } tsconfig.json:: @@ -253,22 +273,17 @@ Signatures:: (computed .d.ts) /home/src/workspaces/project/repro.ts -Diff:: Like Strada, signature generation produces the missing-global diagnostic before semantic checking, so it is excluded from the file's semantic diagnostics. ---- nonIncremental.output.txt -+++ incremental.output.txt -@@ -1,4 +0,0 @@ --error TS2318: Cannot find global type 'IterableIterator'. -- --Found 1 error. -- - Edit [2]:: no change tsgo -ExitStatus:: Success +ExitStatus:: DiagnosticsPresent_OutputsGenerated Output:: +error TS2318: Cannot find global type 'IterableIterator'. + +Found 1 error. + //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"303d5119b8941b887ef975780b83caec-export function* values() { yield 1; }\n// comment-only edit\n","signature":"b9f7a0d32d9887fdd864bf1c0dcd3591-export declare function values(): {};\n","impliedNodeFormat":1}],"affectedFilesPendingEmit":[2]} +{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"8859c12c614ce56ba9a18e58384a198f-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray {}\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"303d5119b8941b887ef975780b83caec-export function* values() { yield 1; }\n// comment-only edit\n","signature":"b9f7a0d32d9887fdd864bf1c0dcd3591-export declare function values(): {};\n","impliedNodeFormat":1}],"semanticDiagnosticsPerFile":[[2,[{"noFile":true,"code":2318,"category":1,"messageKey":"Cannot_find_global_type_0_2318","messageArgs":["IterableIterator"]}]]],"affectedFilesPendingEmit":[2]} //// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* { "version": "FakeTSVersion", @@ -309,6 +324,22 @@ Output:: } } ], + "semanticDiagnosticsPerFile": [ + [ + "./repro.ts", + [ + { + "noFile": true, + "code": 2318, + "category": 1, + "messageKey": "Cannot_find_global_type_0_2318", + "messageArgs": [ + "IterableIterator" + ] + } + ] + ] + ], "affectedFilesPendingEmit": [ [ "./repro.ts", @@ -316,7 +347,7 @@ Output:: 2 ] ], - "size": 1122 + "size": 1281 } tsconfig.json:: @@ -324,15 +355,6 @@ SemanticDiagnostics:: Signatures:: -Diff:: Like Strada, the cached semantic diagnostics do not include the missing-global diagnostic produced during signature generation. ---- nonIncremental.output.txt -+++ incremental.output.txt -@@ -1,4 +0,0 @@ --error TS2318: Cannot find global type 'IterableIterator'. -- --Found 1 error. -- - Edit [3]:: delete build info to restore the semantic diagnostic //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *deleted* diff --git a/tsc/testdata/baselines/reference/tsc/incremental/recursive-mapped-type-after-comment-only-edit.js b/tsc/testdata/baselines/reference/tsc/incremental/recursive-mapped-type-after-comment-only-edit.js index 7e3f0da04d4e6..610f7fa7e72b5 100644 --- a/tsc/testdata/baselines/reference/tsc/incremental/recursive-mapped-type-after-comment-only-edit.js +++ b/tsc/testdata/baselines/reference/tsc/incremental/recursive-mapped-type-after-comment-only-edit.js @@ -105,11 +105,10 @@ tsgo ExitStatus:: Success Output:: //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","errors":true,"root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"05e6c83fc809e0d08962e5b3425da798-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n","signature":"93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} +{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"05e6c83fc809e0d08962e5b3425da798-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n","signature":"93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} //// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* { "version": "FakeTSVersion", - "errors": true, "root": [ { "files": [ @@ -157,7 +156,7 @@ Output:: 2 ] ], - "size": 1437 + "size": 1423 } tsconfig.json:: @@ -172,60 +171,6 @@ Edit [1]:: no change tsgo ExitStatus:: Success Output:: -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"05e6c83fc809e0d08962e5b3425da798-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n","signature":"93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* -{ - "version": "FakeTSVersion", - "root": [ - { - "files": [ - "./repro.ts" - ], - "original": 2 - } - ], - "fileNames": [ - "lib.es2026.full.d.ts", - "./repro.ts" - ], - "fileInfos": [ - { - "fileName": "lib.es2026.full.d.ts", - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "signature": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": "CommonJS", - "original": { - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": 1 - } - }, - { - "fileName": "./repro.ts", - "version": "05e6c83fc809e0d08962e5b3425da798-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n", - "signature": "93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n", - "impliedNodeFormat": "CommonJS", - "original": { - "version": "05e6c83fc809e0d08962e5b3425da798-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n", - "signature": "93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n", - "impliedNodeFormat": 1 - } - } - ], - "options": { - "strict": true - }, - "affectedFilesPendingEmit": [ - [ - "./repro.ts", - "Js", - 2 - ] - ], - "size": 1423 -} tsconfig.json:: SemanticDiagnostics:: @@ -247,11 +192,10 @@ tsgo ExitStatus:: Success Output:: //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","errors":true,"root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"989f15878b40fd4e572d65e47a8cc845-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n\n// another comment\n","signature":"93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} +{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"989f15878b40fd4e572d65e47a8cc845-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n\n// another comment\n","signature":"93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} //// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* { "version": "FakeTSVersion", - "errors": true, "root": [ { "files": [ @@ -299,7 +243,7 @@ Output:: 2 ] ], - "size": 1459 + "size": 1445 } tsconfig.json:: @@ -314,60 +258,6 @@ Edit [3]:: no change tsgo ExitStatus:: Success Output:: -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"989f15878b40fd4e572d65e47a8cc845-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n\n// another comment\n","signature":"93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* -{ - "version": "FakeTSVersion", - "root": [ - { - "files": [ - "./repro.ts" - ], - "original": 2 - } - ], - "fileNames": [ - "lib.es2026.full.d.ts", - "./repro.ts" - ], - "fileInfos": [ - { - "fileName": "lib.es2026.full.d.ts", - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "signature": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": "CommonJS", - "original": { - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": 1 - } - }, - { - "fileName": "./repro.ts", - "version": "989f15878b40fd4e572d65e47a8cc845-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n\n// another comment\n", - "signature": "93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n", - "impliedNodeFormat": "CommonJS", - "original": { - "version": "989f15878b40fd4e572d65e47a8cc845-type Json = string | Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as Json[] });\n// comment-only edit\n\n// another comment\n", - "signature": "93e92d0786a923286e91eb7834b5ae1a-type Json = string | Json[];\nexport declare const value: {\n items: Json[];\n};\nexport {};\n", - "impliedNodeFormat": 1 - } - } - ], - "options": { - "strict": true - }, - "affectedFilesPendingEmit": [ - [ - "./repro.ts", - "Js", - 2 - ] - ], - "size": 1445 -} tsconfig.json:: SemanticDiagnostics:: diff --git a/tsc/testdata/baselines/reference/tsc/incremental/recursive-readonly-mapped-type-after-comment-only-edit.js b/tsc/testdata/baselines/reference/tsc/incremental/recursive-readonly-mapped-type-after-comment-only-edit.js index 009ea164deed4..211fd77b970ec 100644 --- a/tsc/testdata/baselines/reference/tsc/incremental/recursive-readonly-mapped-type-after-comment-only-edit.js +++ b/tsc/testdata/baselines/reference/tsc/incremental/recursive-readonly-mapped-type-after-comment-only-edit.js @@ -105,11 +105,10 @@ tsgo ExitStatus:: Success Output:: //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","errors":true,"root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"44adeee334483dab0e3927db949cf78a-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n","signature":"ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} +{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"44adeee334483dab0e3927db949cf78a-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n","signature":"ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} //// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* { "version": "FakeTSVersion", - "errors": true, "root": [ { "files": [ @@ -157,7 +156,7 @@ Output:: 2 ] ], - "size": 1473 + "size": 1459 } tsconfig.json:: @@ -172,60 +171,6 @@ Edit [1]:: no change tsgo ExitStatus:: Success Output:: -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"44adeee334483dab0e3927db949cf78a-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n","signature":"ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* -{ - "version": "FakeTSVersion", - "root": [ - { - "files": [ - "./repro.ts" - ], - "original": 2 - } - ], - "fileNames": [ - "lib.es2026.full.d.ts", - "./repro.ts" - ], - "fileInfos": [ - { - "fileName": "lib.es2026.full.d.ts", - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "signature": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": "CommonJS", - "original": { - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": 1 - } - }, - { - "fileName": "./repro.ts", - "version": "44adeee334483dab0e3927db949cf78a-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n", - "signature": "ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n", - "impliedNodeFormat": "CommonJS", - "original": { - "version": "44adeee334483dab0e3927db949cf78a-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n", - "signature": "ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n", - "impliedNodeFormat": 1 - } - } - ], - "options": { - "strict": true - }, - "affectedFilesPendingEmit": [ - [ - "./repro.ts", - "Js", - 2 - ] - ], - "size": 1459 -} tsconfig.json:: SemanticDiagnostics:: @@ -247,11 +192,10 @@ tsgo ExitStatus:: Success Output:: //// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","errors":true,"root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"123beaefc70ba63a9447b94fb12d09b5-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n\n// another comment\n","signature":"ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} +{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"123beaefc70ba63a9447b94fb12d09b5-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n\n// another comment\n","signature":"ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} //// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* { "version": "FakeTSVersion", - "errors": true, "root": [ { "files": [ @@ -299,7 +243,7 @@ Output:: 2 ] ], - "size": 1495 + "size": 1481 } tsconfig.json:: @@ -314,60 +258,6 @@ Edit [3]:: no change tsgo ExitStatus:: Success Output:: -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo] *modified* -{"version":"FakeTSVersion","root":[2],"fileNames":["lib.es2026.full.d.ts","./repro.ts"],"fileInfos":[{"version":"0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true,"impliedNodeFormat":1},{"version":"123beaefc70ba63a9447b94fb12d09b5-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n\n// another comment\n","signature":"ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n","impliedNodeFormat":1}],"options":{"strict":true},"affectedFilesPendingEmit":[2]} -//// [/home/src/workspaces/project/tsconfig.tsbuildinfo.readable.baseline.txt] *modified* -{ - "version": "FakeTSVersion", - "root": [ - { - "files": [ - "./repro.ts" - ], - "original": 2 - } - ], - "fileNames": [ - "lib.es2026.full.d.ts", - "./repro.ts" - ], - "fileInfos": [ - { - "fileName": "lib.es2026.full.d.ts", - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "signature": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": "CommonJS", - "original": { - "version": "0e330bc1e98a7e2601e4b38548df5e2b-/// \ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array { length: number; [n: number]: T; }\ninterface ReadonlyArray { readonly length: number; readonly [n: number]: T; }\ninterface SymbolConstructor {\n (desc?: string | number): symbol;\n for(name: string): symbol;\n readonly toStringTag: symbol;\n}\ndeclare var Symbol: SymbolConstructor;\ninterface Symbol {\n readonly [Symbol.toStringTag]: string;\n}\ndeclare const console: { log(msg: any): void; };", - "affectsGlobalScope": true, - "impliedNodeFormat": 1 - } - }, - { - "fileName": "./repro.ts", - "version": "123beaefc70ba63a9447b94fb12d09b5-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n\n// another comment\n", - "signature": "ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n", - "impliedNodeFormat": "CommonJS", - "original": { - "version": "123beaefc70ba63a9447b94fb12d09b5-type Json = string | readonly Json[];\ntype Parsed = T extends object ? { [K in keyof T]: Parsed } : T;\ndeclare function wrap(value: T): Parsed;\nexport const value = wrap({ items: [] as readonly Json[] });\n// comment-only edit\n\n// another comment\n", - "signature": "ac9ba36fbad04eb70b8f2b14cdf772ef-type Json = string | readonly Json[];\nexport declare const value: {\n items: readonly Json[];\n};\nexport {};\n", - "impliedNodeFormat": 1 - } - } - ], - "options": { - "strict": true - }, - "affectedFilesPendingEmit": [ - [ - "./repro.ts", - "Js", - 2 - ] - ], - "size": 1481 -} tsconfig.json:: SemanticDiagnostics:: diff --git a/tsc/testdata/tests/cases/compiler/contextualCheckInDiscardedCycle.ts b/tsc/testdata/tests/cases/compiler/contextualCheckInDiscardedCycle.ts new file mode 100644 index 0000000000000..f7ecd882156e7 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/contextualCheckInDiscardedCycle.ts @@ -0,0 +1,39 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// @filename: returnTypeCycle.ts +// Callbacks contextually checked while a cycle is in progress are checked +// again once it is discarded, with their contextual parameter types. + +declare function useCallback(callback: T): T; +declare function refreshAll(options: { ids: string[]; extra: T }): Promise; + +interface View { + refresh: (force?: boolean) => Promise; +} + +declare const items: { id: string }[]; + +function useView(): View { + const refresh = useCallback((force?: boolean) => + refreshAll({ + ids: items.map(({ id }) => id), + extra: 1, + })); + return { refresh }; +} + +// @filename: yieldOperandCycle.ts +interface Eff { + [Symbol.iterator](): Generator, A, any>; + pipe(f: (self: Eff) => B): B; +} +declare function mapError(f: (e: E) => E2): (self: Eff) => Eff; +declare function fn(body: (input: number) => Generator): (input: number) => Eff; +declare function make(s: T): T; +declare const uuid: Eff; + +fn(function* (input) { + make(yield* uuid.pipe(mapError((cause) => ({ cause })))); +}); diff --git a/tsc/testdata/tests/cases/compiler/contextualCheckReentryAfterParameters.ts b/tsc/testdata/tests/cases/compiler/contextualCheckReentryAfterParameters.ts new file mode 100644 index 0000000000000..3d4cac45ed898 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/contextualCheckReentryAfterParameters.ts @@ -0,0 +1,41 @@ +// @strict: true +// @noEmit: true + +// Instantiating the type predicate inside the nested callback re-enters the +// contextual check of the enclosing functions through their outer type +// parameters. That must not discard the parameter types assigned to them. + +type Surface = { id: string; kind: "file"; path: string } | { id: string; kind: "terminal" }; +interface Panel { + surfaces: Surface[]; +} +interface Store { + panel: Panel; + openFile: (path: string) => void; + close: (id: string) => void; +} +type Creator = (set: (update: (state: T) => Partial) => void) => T; +declare function create(init: Creator): T; +declare function wrap(init: Creator): Creator; + +const update = (state: Store, updater: (current: Panel) => Panel): Partial => ({ panel: updater(state.panel) }); + +export const store = create(wrap((set) => ({ + panel: { surfaces: [] }, + openFile: (path) => + set((state) => + update(state, (current) => { + const existing = current.surfaces.find( + (surface): surface is Extract => surface.kind === "file" && surface.path === path, + ); + return existing ? current : { surfaces: [...current.surfaces, { id: path, kind: "file", path }] }; + }) + ), + close: (id) => + set((state) => + update(state, (current) => { + const index = current.surfaces.findIndex((entry) => entry.id === id); + return { surfaces: current.surfaces.slice(index) }; + }) + ), +}))); diff --git a/tsc/testdata/tests/cases/compiler/contextualReturnTypeInSpreadArgument.ts b/tsc/testdata/tests/cases/compiler/contextualReturnTypeInSpreadArgument.ts new file mode 100644 index 0000000000000..d047462f7c312 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/contextualReturnTypeInSpreadArgument.ts @@ -0,0 +1,18 @@ +// @strict: true + +enum Kind { A, B } + +interface ItemA { kind: Kind.A; name: string } +interface ItemB { kind: Kind.B; size: number } +type Item = ItemA | ItemB; + +declare function create(options: { items?: () => readonly Item[]; label: string }): void; + +function test(flag: boolean) { + create({ + label: "x", + ...(flag ? {} : { + items: () => [{ kind: Kind.A, name: "a" }], + }), + }); +} diff --git a/tsc/testdata/tests/cases/compiler/jsExpandoIndexedAssignmentDiagnostic.ts b/tsc/testdata/tests/cases/compiler/jsExpandoIndexedAssignmentDiagnostic.ts new file mode 100644 index 0000000000000..33c25e649c64e --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/jsExpandoIndexedAssignmentDiagnostic.ts @@ -0,0 +1,13 @@ +// @target: esnext +// @checkJs: true +// @noImplicitAny: true +// @outDir: out +// @filename: file.js + +/** @param {any} axis @param {any} value */ +function updateElements(axis, value) { + const properties = {}; + const pixel = properties[axis] = value; + const otherPixel = properties[axis] = value; + properties.skip = isNaN(pixel) || isNaN(otherPixel); +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/lateBoundMutualMemberTables.ts b/tsc/testdata/tests/cases/compiler/lateBoundMutualMemberTables.ts new file mode 100644 index 0000000000000..24336a6a9155b --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/lateBoundMutualMemberTables.ts @@ -0,0 +1,20 @@ +// @target: esnext +// @strict: true +// @declaration: true + +declare const a: A<"a">; +declare const b: B<"b">; +declare class A { + x: T; + [b.y]: "fromB"; + [b.by]: "fromB2"; +} +declare class B { + y: T; + [a.x]: "fromA"; + [a.b]: "fromA2"; +} +export const v1 = a.b; +export const v2 = b.a; +export const v3 = a.fromA2; +export const v4 = b.fromB;