Skip to content

Commit 7337902

Browse files
committed
Check never-reducing intersection properties without creating them
getReducedType runs somePropertyReducesToNever on intersections. It used to create and cache a synthetic property for every property name shared by more than one constituent, only to inspect its flags. With large JSX prop intersections, as in MUI, most of those properties were never looked up again, yet they stayed in the intersection's property cache with their symbols and links. Collect the constituent properties without building the combined symbol, and only create the real property in the rare merged instantiation case. mui-docs: │ before │ after │ │ memory-used-B │ memory-used-B vs base │ MUIDocs 1.857Gi ± 0% 1.717Gi ± 0% -7.55% (p=0.002 n=6) │ before │ after │ │ allocs │ allocs vs base │ MUIDocs 27.29M ± 0% 25.19M ± 0% -7.72% (p=0.002 n=6) │ before │ after │ │ allocated-B │ allocated-B vs base │ MUIDocs 4.730Gi ± 0% 4.556Gi ± 0% -3.68% (p=0.002 n=6) │ before │ after │ │ symbols │ symbols vs base │ MUIDocs 4.332M ± 0% 3.663M ± 0% -15.43% (p=0.002 n=6)
1 parent 6aa42dc commit 7337902

1 file changed

Lines changed: 110 additions & 10 deletions

File tree

‎tsc/internal/checker/checker.go‎

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21950,15 +21950,34 @@ func (c *Checker) getUnionOrIntersectionProperty(t *Type, name string, skipObjec
2195021950
return prop
2195121951
}
2195221952

21953-
func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol {
21953+
// unionOrIntersectionPropertySources describes the constituent properties from
21954+
// which a union or intersection property would be synthesized.
21955+
type unionOrIntersectionPropertySources struct {
21956+
singleProp *ast.Symbol
21957+
propSet collections.OrderedSet[*ast.Symbol]
21958+
indexTypes []*Type
21959+
propFlags ast.SymbolFlags
21960+
optionalFlag ast.SymbolFlags
21961+
checkFlags ast.CheckFlags
21962+
syntheticFlag ast.CheckFlags
21963+
mergedInstantiations bool
21964+
}
21965+
21966+
// isSynthetic reports whether the sources require a new synthetic symbol rather
21967+
// than reusing (or cloning) singleProp.
21968+
func (s *unionOrIntersectionPropertySources) isSynthetic() bool {
21969+
return s.propSet.Size() != 0 || s.checkFlags&ast.CheckFlagsReadPartial != 0 || len(s.indexTypes) != 0
21970+
}
21971+
21972+
func (c *Checker) getUnionOrIntersectionPropertySources(containingType *Type, name string, skipObjectFunctionPropertyAugment bool, sources *unionOrIntersectionPropertySources) bool {
2195421973
propFlags := ast.SymbolFlagsNone
21955-
var singleProp *ast.Symbol
21956-
var propSet collections.OrderedSet[*ast.Symbol]
21957-
var indexTypes []*Type
2195821974
isUnion := containingType.flags&TypeFlagsUnion != 0
2195921975
// Flags we want to propagate to the result if they exist in all source symbols
2196021976
var checkFlags ast.CheckFlags
2196121977
var optionalFlag ast.SymbolFlags
21978+
var singleProp *ast.Symbol
21979+
propSet := &sources.propSet
21980+
var indexTypes []*Type
2196221981
if !isUnion {
2196321982
checkFlags = ast.CheckFlagsReadonly
2196421983
optionalFlag = ast.SymbolFlagsOptional
@@ -22060,17 +22079,17 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s
2206022079
}
2206122080
if singleProp == nil {
2206222081
// No property was found
22063-
return nil
22082+
return false
2206422083
}
2206522084
if isUnion &&
2206622085
(propSet.Size() != 0 || checkFlags&ast.CheckFlagsPartial != 0) &&
2206722086
checkFlags&(ast.CheckFlagsContainsPrivate|ast.CheckFlagsContainsProtected|ast.CheckFlagsContainsWritePrivate|ast.CheckFlagsContainsWriteProtected) != 0 &&
22068-
!(propSet.Size() != 0 && c.hasCommonDeclaration(&propSet)) {
22087+
!(propSet.Size() != 0 && c.hasCommonDeclaration(propSet)) {
2206922088
// A property in a union has a private or protected declaration in one constituent, but is missing
2207022089
// or has a different declaration in another constituent. If the private or protected declaration is
2207122090
// for reading, we don't create a property.
2207222091
if checkFlags&(ast.CheckFlagsContainsPrivate|ast.CheckFlagsContainsProtected) != 0 {
22073-
return nil
22092+
return false
2207422093
}
2207522094
// Otherwise, if the private or protected declaration is for writing, reduce accessibility to that of
2207622095
// the most restricted constituent.
@@ -22080,8 +22099,31 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s
2208022099
checkFlags &^= ast.CheckFlagsContainsWritePublic
2208122100
}
2208222101
}
22083-
if propSet.Size() == 0 && checkFlags&ast.CheckFlagsReadPartial == 0 && len(indexTypes) == 0 {
22084-
if !mergedInstantiations {
22102+
sources.singleProp = singleProp
22103+
sources.indexTypes = indexTypes
22104+
sources.propFlags = propFlags
22105+
sources.optionalFlag = optionalFlag
22106+
sources.checkFlags = checkFlags
22107+
sources.syntheticFlag = syntheticFlag
22108+
sources.mergedInstantiations = mergedInstantiations
22109+
return true
22110+
}
22111+
22112+
func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol {
22113+
var sources unionOrIntersectionPropertySources
22114+
if !c.getUnionOrIntersectionPropertySources(containingType, name, skipObjectFunctionPropertyAugment, &sources) {
22115+
return nil
22116+
}
22117+
singleProp := sources.singleProp
22118+
propSet := &sources.propSet
22119+
indexTypes := sources.indexTypes
22120+
propFlags := sources.propFlags
22121+
optionalFlag := sources.optionalFlag
22122+
checkFlags := sources.checkFlags
22123+
syntheticFlag := sources.syntheticFlag
22124+
isUnion := containingType.flags&TypeFlagsUnion != 0
22125+
if !sources.isSynthetic() {
22126+
if !sources.mergedInstantiations {
2208522127
return singleProp
2208622128
}
2208722129
// No symbol from a union/intersection should have a `.parent` set (since unions/intersections don't act as symbol parents)
@@ -22390,14 +22432,72 @@ func (c *Checker) somePropertyReducesToNever(t *Type) bool {
2239022432
// Go in the order the properties were found so the combined properties are created in the same order every time.
2239122433
for propName, count := range counts.Entries() {
2239222434
if count > 1 {
22393-
if prop := c.getPropertyOfUnionOrIntersectionType(t, propName, true /*skipObjectFunctionPropertyAugment*/); prop != nil && c.isNeverReducedProperty(prop) {
22435+
if c.intersectionPropertyReducesToNever(t, propName) {
2239422436
return true
2239522437
}
2239622438
}
2239722439
}
2239822440
return false
2239922441
}
2240022442

22443+
// intersectionPropertyReducesToNever reports whether the combined property named
22444+
// name would satisfy isNeverReducedProperty, without creating and caching that
22445+
// property. Most such properties are never otherwise looked up, so creating
22446+
// them only to inspect their flags retains a large number of symbols.
22447+
func (c *Checker) intersectionPropertyReducesToNever(t *Type, name string) bool {
22448+
if prop := t.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment[name]; prop != nil {
22449+
return c.isNeverReducedProperty(prop)
22450+
}
22451+
var sources unionOrIntersectionPropertySources
22452+
if !c.getUnionOrIntersectionPropertySources(t, name, true /*skipObjectFunctionPropertyAugment*/, &sources) {
22453+
return false
22454+
}
22455+
if !sources.isSynthetic() {
22456+
if !sources.mergedInstantiations {
22457+
return c.isNeverReducedProperty(sources.singleProp)
22458+
}
22459+
prop := c.getUnionOrIntersectionProperty(t, name, true /*skipObjectFunctionPropertyAugment*/)
22460+
return prop != nil && c.isNeverReducedProperty(prop)
22461+
}
22462+
if sources.propSet.Size() == 0 {
22463+
sources.propSet.Add(sources.singleProp)
22464+
}
22465+
checkFlags := sources.checkFlags
22466+
var firstType *Type
22467+
var firstValueDeclaration *ast.Node
22468+
hasNonUniformValueDeclaration := false
22469+
propTypes := make([]*Type, 0, sources.propSet.Size()+len(sources.indexTypes))
22470+
for prop := range sources.propSet.Values() {
22471+
if firstValueDeclaration == nil {
22472+
firstValueDeclaration = prop.ValueDeclaration()
22473+
} else if prop.ValueDeclaration() != nil && prop.ValueDeclaration() != firstValueDeclaration {
22474+
hasNonUniformValueDeclaration = true
22475+
}
22476+
propType := c.getTypeOfSymbol(prop)
22477+
if firstType == nil {
22478+
firstType = propType
22479+
}
22480+
if propType != firstType {
22481+
checkFlags |= ast.CheckFlagsHasNonUniformType
22482+
}
22483+
if isLiteralType(propType) || c.isPatternLiteralType(propType) {
22484+
checkFlags |= ast.CheckFlagsHasLiteralType
22485+
}
22486+
if propType.flags&TypeFlagsNever != 0 && propType != c.uniqueLiteralType {
22487+
checkFlags |= ast.CheckFlagsHasNeverType
22488+
}
22489+
propTypes = append(propTypes, propType)
22490+
}
22491+
propTypes = append(propTypes, sources.indexTypes...)
22492+
hasValueDeclaration := !hasNonUniformValueDeclaration && firstValueDeclaration != nil
22493+
if !hasValueDeclaration && checkFlags&ast.CheckFlagsContainsPrivate != 0 {
22494+
return true
22495+
}
22496+
return (sources.propFlags|sources.optionalFlag)&ast.SymbolFlagsOptional == 0 &&
22497+
checkFlags&(ast.CheckFlagsNonUniformAndLiteral|ast.CheckFlagsHasNeverType) == ast.CheckFlagsNonUniformAndLiteral &&
22498+
c.getIntersectionType(propTypes).flags&TypeFlagsNever != 0
22499+
}
22500+
2240122501
func (c *Checker) getReducedUnionType(unionType *Type) *Type {
2240222502
reducedTypes := core.SameMap(unionType.Types(), c.getReducedType)
2240322503
if core.Same(reducedTypes, unionType.Types()) {

0 commit comments

Comments
 (0)