Skip to content

Commit 2993ea8

Browse files
authored
Monomorphic Symbol access (#51880)
1 parent b9aa8a4 commit 2993ea8

File tree

11 files changed

+185
-150
lines changed

11 files changed

+185
-150
lines changed

src/compiler/checker.ts

Lines changed: 118 additions & 107 deletions
Large diffs are not rendered by default.

src/compiler/types.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5744,19 +5744,20 @@ export interface Symbol {
57445744
members?: SymbolTable; // Class, interface or object literal instance members
57455745
exports?: SymbolTable; // Module exports
57465746
globalExports?: SymbolTable; // Conditional global UMD exports
5747-
/** @internal */ id?: SymbolId; // Unique id (used to look up SymbolLinks)
5748-
/** @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
5749-
/** @internal */ parent?: Symbol; // Parent symbol
5750-
/** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
5751-
/** @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
5747+
/** @internal */ id: SymbolId; // Unique id (used to look up SymbolLinks)
5748+
/** @internal */ mergeId: number; // Merge id (used to look up merged symbol)
5749+
/** @internal */ parent?: Symbol; // Parent symbol
5750+
/** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
5751+
/** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums
57525752
/** @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter.
57535753
/** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol?
5754-
/** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments
5754+
/** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments
57555755
/** @internal */ assignmentDeclarationMembers?: Map<number, Declaration>; // detected late-bound assignment declarations associated with the symbol
57565756
}
57575757

57585758
/** @internal */
57595759
export interface SymbolLinks {
5760+
_symbolLinksBrand: any;
57605761
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
57615762
aliasTarget?: Symbol, // Resolved (non-alias) target of an alias
57625763
target?: Symbol; // Original version of an instantiated symbol
@@ -5767,7 +5768,7 @@ export interface SymbolLinks {
57675768
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter
57685769
typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic)
57695770
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
5770-
instantiations?: Map<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
5771+
instantiations?: Map<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
57715772
aliasSymbol?: Symbol; // Alias associated with generic type alias instantiation
57725773
aliasTypeArguments?: readonly Type[] // Alias type arguments (if any)
57735774
inferredClassSymbol?: Map<SymbolId, TransientSymbol>; // Symbol of an inferred ES5 constructor function
@@ -5840,23 +5841,38 @@ export const enum CheckFlags {
58405841
}
58415842

58425843
/** @internal */
5843-
export interface TransientSymbol extends Symbol, SymbolLinks {
5844+
export interface TransientSymbolLinks extends SymbolLinks {
58445845
checkFlags: CheckFlags;
58455846
}
58465847

58475848
/** @internal */
5848-
export interface MappedSymbol extends TransientSymbol {
5849+
export interface TransientSymbol extends Symbol {
5850+
links: TransientSymbolLinks;
5851+
}
5852+
5853+
/** @internal */
5854+
export interface MappedSymbolLinks extends TransientSymbolLinks {
58495855
mappedType: MappedType;
58505856
keyType: Type;
58515857
}
58525858

58535859
/** @internal */
5854-
export interface ReverseMappedSymbol extends TransientSymbol {
5860+
export interface MappedSymbol extends TransientSymbol {
5861+
links: MappedSymbolLinks;
5862+
}
5863+
5864+
/** @internal */
5865+
export interface ReverseMappedSymbolLinks extends TransientSymbolLinks {
58555866
propertyType: Type;
58565867
mappedType: MappedType;
58575868
constraintType: IndexType;
58585869
}
58595870

5871+
/** @internal */
5872+
export interface ReverseMappedSymbol extends TransientSymbol {
5873+
links: ReverseMappedSymbolLinks;
5874+
}
5875+
58605876
export const enum InternalSymbolName {
58615877
Call = "__call", // Call signatures
58625878
Constructor = "__constructor", // Constructor implementations

src/compiler/utilities.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6825,7 +6825,7 @@ export function closeFileWatcher(watcher: FileWatcher) {
68256825

68266826
/** @internal */
68276827
export function getCheckFlags(symbol: Symbol): CheckFlags {
6828-
return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).checkFlags : 0;
6828+
return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).links.checkFlags : 0;
68296829
}
68306830

68316831
/** @internal */
@@ -6837,7 +6837,8 @@ export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false
68376837
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
68386838
}
68396839
if (getCheckFlags(s) & CheckFlags.Synthetic) {
6840-
const checkFlags = (s as TransientSymbol).checkFlags;
6840+
// NOTE: potentially unchecked cast to TransientSymbol
6841+
const checkFlags = (s as TransientSymbol).links.checkFlags;
68416842
const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private :
68426843
checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public :
68436844
ModifierFlags.Protected;
@@ -7266,9 +7267,16 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) {
72667267
this.escapedName = name;
72677268
this.declarations = undefined;
72687269
this.valueDeclaration = undefined;
7269-
this.id = undefined;
7270-
this.mergeId = undefined;
7270+
this.id = 0;
7271+
this.mergeId = 0;
72717272
this.parent = undefined;
7273+
this.members = undefined;
7274+
this.exports = undefined;
7275+
this.exportSymbol = undefined;
7276+
this.constEnumOnlyModule = undefined;
7277+
this.isReferenced = undefined;
7278+
this.isAssigned = undefined;
7279+
(this as any).links = undefined; // used by TransientSymbol
72727280
}
72737281

72747282
function Type(this: Type, checker: TypeChecker, flags: TypeFlags) {

src/services/codefixes/fixInvalidImportSyntax.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
isExpression,
1616
isImportCall,
1717
isNamedDeclaration,
18+
isTransientSymbol,
1819
makeImport,
1920
ModuleKind,
2021
NamespaceImport,
@@ -23,7 +24,6 @@ import {
2324
SourceFile,
2425
SyntaxKind,
2526
textChanges,
26-
TransientSymbol,
2727
} from "../_namespaces/ts";
2828
import {
2929
createCodeFixActionWithoutFixAll,
@@ -107,11 +107,11 @@ function getActionsForInvalidImportLocation(context: CodeFixContext): CodeFixAct
107107

108108
function getImportCodeFixesForExpression(context: CodeFixContext, expr: Node): CodeFixAction[] | undefined {
109109
const type = context.program.getTypeChecker().getTypeAtLocation(expr);
110-
if (!(type.symbol && (type.symbol as TransientSymbol).originatingImport)) {
110+
if (!(type.symbol && isTransientSymbol(type.symbol) && type.symbol.links.originatingImport)) {
111111
return [];
112112
}
113113
const fixes: CodeFixAction[] = [];
114-
const relatedImport = (type.symbol as TransientSymbol).originatingImport!; // TODO: GH#18217
114+
const relatedImport = type.symbol.links.originatingImport;
115115
if (!isImportCall(relatedImport)) {
116116
addRange(fixes, getCodeFixesForImportDeclaration(context, relatedImport));
117117
}

src/services/codefixes/inferFromUsage.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
isRestParameter,
5454
isRightSideOfQualifiedNameOrPropertyAccess,
5555
isSetAccessorDeclaration,
56+
isTransientSymbol,
5657
isVariableDeclaration,
5758
isVariableStatement,
5859
LanguageServiceHost,
@@ -88,7 +89,6 @@ import {
8889
SourceFile,
8990
Symbol,
9091
SymbolFlags,
91-
SymbolLinks,
9292
SyntaxKind,
9393
textChanges,
9494
Token,
@@ -1045,7 +1045,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi
10451045
const members = mapEntries(props, (name, types) => {
10461046
const isOptional = types.length < anons.length ? SymbolFlags.Optional : 0;
10471047
const s = checker.createSymbol(SymbolFlags.Property | isOptional, name as __String);
1048-
s.type = checker.getUnionType(types);
1048+
s.links.type = checker.getUnionType(types);
10491049
return [name, s];
10501050
});
10511051
const indexInfos = [];
@@ -1080,7 +1080,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi
10801080

10811081
const candidateTypes = (usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t));
10821082
const callsType = usage.calls?.length ? inferStructuralType(usage) : undefined;
1083-
if (callsType && candidateTypes) {
1083+
if (callsType && candidateTypes) { // TODO: should this be `some(candidateTypes)`?
10841084
types.push(checker.getUnionType([callsType, ...candidateTypes], UnionReduction.Subtype));
10851085
}
10861086
else {
@@ -1101,7 +1101,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi
11011101
if (usage.properties) {
11021102
usage.properties.forEach((u, name) => {
11031103
const symbol = checker.createSymbol(SymbolFlags.Property, name);
1104-
symbol.type = combineFromUsage(u);
1104+
symbol.links.type = combineFromUsage(u);
11051105
members.set(name, symbol);
11061106
});
11071107
}
@@ -1202,7 +1202,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi
12021202
if (elementType) {
12031203
genericParamType = elementType;
12041204
}
1205-
const targetType = (usageParam as SymbolLinks).type
1205+
const targetType = tryCast(usageParam, isTransientSymbol)?.links.type
12061206
|| (usageParam.valueDeclaration ? checker.getTypeOfSymbolAtLocation(usageParam, usageParam.valueDeclaration) : checker.getAnyType());
12071207
types.push(...inferTypeParameters(genericParamType, targetType, typeParameter));
12081208
}
@@ -1221,7 +1221,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi
12211221
const length = Math.max(...calls.map(c => c.argumentTypes.length));
12221222
for (let i = 0; i < length; i++) {
12231223
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
1224-
symbol.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType()));
1224+
symbol.links.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType()));
12251225
if (calls.some(call => call.argumentTypes[i] === undefined)) {
12261226
symbol.flags |= SymbolFlags.Optional;
12271227
}

src/services/codefixes/returnValueCorrect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ registerCodeFix({
120120

121121
function createObjectTypeFromLabeledExpression(checker: TypeChecker, label: Identifier, expression: Expression) {
122122
const member = checker.createSymbol(SymbolFlags.Property, label.escapedText);
123-
member.type = checker.getTypeAtLocation(expression);
123+
member.links.type = checker.getTypeAtLocation(expression);
124124
const members = createSymbolTable([member]);
125125
return checker.createAnonymousType(/*symbol*/ undefined, members, [], [], []);
126126
}

src/services/refactors/extractSymbol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2124,7 +2124,7 @@ function collectReadsAndWrites(
21242124
const decl = find(visibleDeclarationsInExtractedRange, d => d.symbol === sym);
21252125
if (decl) {
21262126
if (isVariableDeclaration(decl)) {
2127-
const idString = decl.symbol.id!.toString();
2127+
const idString = decl.symbol.id.toString();
21282128
if (!exposedVariableSymbolSet.has(idString)) {
21292129
exposedVariableDeclarations.push(decl);
21302130
exposedVariableSymbolSet.set(idString, true);

src/services/services.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ import {
183183
isTagName,
184184
isTextWhiteSpaceLike,
185185
isThisTypeParameter,
186-
JSDoc,
186+
isTransientSymbol,
187187
JsDoc,
188+
JSDoc,
188189
JSDocContainer,
189190
JSDocTagInfo,
190191
JsonSourceFile,
@@ -304,7 +305,6 @@ import {
304305
toPath,
305306
tracing,
306307
TransformFlags,
307-
TransientSymbol,
308308
Type,
309309
TypeChecker,
310310
TypeFlags,
@@ -615,6 +615,9 @@ class SymbolObject implements Symbol {
615615
escapedName: __String;
616616
declarations!: Declaration[];
617617
valueDeclaration!: Declaration;
618+
id = 0;
619+
mergeId = 0;
620+
constEnumOnlyModule: boolean | undefined;
618621

619622
// Undefined is used to indicate the value has not been computed. If, after computing, the
620623
// symbol has no doc comment, then the empty array will be returned.
@@ -656,8 +659,8 @@ class SymbolObject implements Symbol {
656659
if (!this.documentationComment) {
657660
this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs
658661

659-
if (!this.declarations && (this as Symbol as TransientSymbol).target && ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
660-
const labelDecl = ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
662+
if (!this.declarations && isTransientSymbol(this) && this.links.target && isTransientSymbol(this.links.target) && this.links.target.links.tupleLabelDeclaration) {
663+
const labelDecl = this.links.target.links.tupleLabelDeclaration;
661664
this.documentationComment = getDocumentationComment([labelDecl], checker);
662665
}
663666
else {

src/services/signatureHelp.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import {
4949
isTemplateLiteralToken,
5050
isTemplateSpan,
5151
isTemplateTail,
52+
isTransientSymbol,
5253
last,
5354
lastOrUndefined,
5455
ListFormat,
@@ -77,7 +78,6 @@ import {
7778
TaggedTemplateExpression,
7879
TemplateExpression,
7980
TextSpan,
80-
TransientSymbol,
8181
tryCast,
8282
Type,
8383
TypeChecker,
@@ -724,7 +724,7 @@ function itemInfoForParameters(candidateSignature: Signature, checker: TypeCheck
724724
const isVariadic: (parameterList: readonly Symbol[]) => boolean =
725725
!checker.hasEffectiveRestParameter(candidateSignature) ? _ => false
726726
: lists.length === 1 ? _ => true
727-
: pList => !!(pList.length && (pList[pList.length - 1] as TransientSymbol).checkFlags & CheckFlags.RestParameter);
727+
: pList => !!(pList.length && tryCast(pList[pList.length - 1], isTransientSymbol)?.links.checkFlags! & CheckFlags.RestParameter);
728728
return lists.map(parameterList => ({
729729
isVariadic: isVariadic(parameterList),
730730
parameters: parameterList.map(p => createSignatureHelpParameterForParameter(p, checker, enclosingDeclaration, sourceFile, printer)),
@@ -739,7 +739,7 @@ function createSignatureHelpParameterForParameter(parameter: Symbol, checker: Ty
739739
printer.writeNode(EmitHint.Unspecified, param, sourceFile, writer);
740740
});
741741
const isOptional = checker.isOptionalParameter(parameter.valueDeclaration as ParameterDeclaration);
742-
const isRest = !!((parameter as TransientSymbol).checkFlags & CheckFlags.RestParameter);
742+
const isRest = isTransientSymbol(parameter) && !!(parameter.links.checkFlags & CheckFlags.RestParameter);
743743
return { name: parameter.name, documentation: parameter.getDocumentationComment(checker), displayParts, isOptional, isRest };
744744
}
745745

src/services/symbolDisplay.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
isObjectBindingPattern,
5959
isTaggedTemplateExpression,
6060
isThisInTypeQuery,
61+
isTransientSymbol,
6162
isTypeAliasDeclaration,
6263
isVarConst,
6364
JSDocTagInfo,
@@ -176,7 +177,7 @@ function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeCheck
176177
if (flags & SymbolFlags.Signature) return ScriptElementKind.indexSignatureElement;
177178

178179
if (flags & SymbolFlags.Property) {
179-
if (flags & SymbolFlags.Transient && (symbol as TransientSymbol).checkFlags & CheckFlags.Synthetic) {
180+
if (flags & SymbolFlags.Transient && (symbol as TransientSymbol).links.checkFlags & CheckFlags.Synthetic) {
180181
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
181182
const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
182183
const rootSymbolFlags = rootSymbol.getFlags();
@@ -645,8 +646,8 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ
645646
else {
646647
addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration));
647648
}
648-
if ((symbol as TransientSymbol).target && ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
649-
const labelDecl = ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
649+
if (isTransientSymbol(symbol) && symbol.links.target && isTransientSymbol(symbol.links.target) && symbol.links.target.links.tupleLabelDeclaration) {
650+
const labelDecl = symbol.links.target.links.tupleLabelDeclaration;
650651
Debug.assertNode(labelDecl.name, isIdentifier);
651652
displayParts.push(spacePart());
652653
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));

0 commit comments

Comments
 (0)