Skip to content

Commit 6a5956f

Browse files
committed
Monomorphic Symbol access
1 parent 6d41964 commit 6a5956f

11 files changed

+186
-151
lines changed

src/compiler/checker.ts

+119-108
Large diffs are not rendered by default.

src/compiler/types.ts

+26-10
Original file line numberDiff line numberDiff line change
@@ -5735,19 +5735,20 @@ export interface Symbol {
57355735
members?: SymbolTable; // Class, interface or object literal instance members
57365736
exports?: SymbolTable; // Module exports
57375737
globalExports?: SymbolTable; // Conditional global UMD exports
5738-
/** @internal */ id?: SymbolId; // Unique id (used to look up SymbolLinks)
5739-
/** @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
5740-
/** @internal */ parent?: Symbol; // Parent symbol
5741-
/** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
5742-
/** @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums
5738+
/** @internal */ id: SymbolId; // Unique id (used to look up SymbolLinks)
5739+
/** @internal */ mergeId: number; // Merge id (used to look up merged symbol)
5740+
/** @internal */ parent?: Symbol; // Parent symbol
5741+
/** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol
5742+
/** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums
57435743
/** @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.
57445744
/** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol?
5745-
/** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments
5745+
/** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments
57465746
/** @internal */ assignmentDeclarationMembers?: Map<number, Declaration>; // detected late-bound assignment declarations associated with the symbol
57475747
}
57485748

57495749
/** @internal */
57505750
export interface SymbolLinks {
5751+
_symbolLinksBrand: any;
57515752
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
57525753
aliasTarget?: Symbol, // Resolved (non-alias) target of an alias
57535754
target?: Symbol; // Original version of an instantiated symbol
@@ -5758,7 +5759,7 @@ export interface SymbolLinks {
57585759
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter
57595760
typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic)
57605761
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
5761-
instantiations?: Map<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
5762+
instantiations?: Map<string, Type>; // Instantiations of generic type alias (undefined if non-generic)
57625763
aliasSymbol?: Symbol; // Alias associated with generic type alias instantiation
57635764
aliasTypeArguments?: readonly Type[] // Alias type arguments (if any)
57645765
inferredClassSymbol?: Map<SymbolId, TransientSymbol>; // Symbol of an inferred ES5 constructor function
@@ -5831,23 +5832,38 @@ export const enum CheckFlags {
58315832
}
58325833

58335834
/** @internal */
5834-
export interface TransientSymbol extends Symbol, SymbolLinks {
5835+
export interface TransientSymbolLinks extends SymbolLinks {
58355836
checkFlags: CheckFlags;
58365837
}
58375838

58385839
/** @internal */
5839-
export interface MappedSymbol extends TransientSymbol {
5840+
export interface TransientSymbol extends Symbol {
5841+
links: TransientSymbolLinks;
5842+
}
5843+
5844+
/** @internal */
5845+
export interface MappedSymbolLinks extends TransientSymbolLinks {
58405846
mappedType: MappedType;
58415847
keyType: Type;
58425848
}
58435849

58445850
/** @internal */
5845-
export interface ReverseMappedSymbol extends TransientSymbol {
5851+
export interface MappedSymbol extends TransientSymbol {
5852+
links: MappedSymbolLinks;
5853+
}
5854+
5855+
/** @internal */
5856+
export interface ReverseMappedSymbolLinks extends TransientSymbolLinks {
58465857
propertyType: Type;
58475858
mappedType: MappedType;
58485859
constraintType: IndexType;
58495860
}
58505861

5862+
/** @internal */
5863+
export interface ReverseMappedSymbol extends TransientSymbol {
5864+
links: ReverseMappedSymbolLinks;
5865+
}
5866+
58515867
export const enum InternalSymbolName {
58525868
Call = "__call", // Call signatures
58535869
Constructor = "__constructor", // Constructor implementations

src/compiler/utilities.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -6821,7 +6821,7 @@ export function closeFileWatcher(watcher: FileWatcher) {
68216821

68226822
/** @internal */
68236823
export function getCheckFlags(symbol: Symbol): CheckFlags {
6824-
return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).checkFlags : 0;
6824+
return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).links.checkFlags : 0;
68256825
}
68266826

68276827
/** @internal */
@@ -6833,7 +6833,8 @@ export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false
68336833
return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
68346834
}
68356835
if (getCheckFlags(s) & CheckFlags.Synthetic) {
6836-
const checkFlags = (s as TransientSymbol).checkFlags;
6836+
// NOTE: potentially unchecked cast to TransientSymbol
6837+
const checkFlags = (s as TransientSymbol).links.checkFlags;
68376838
const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private :
68386839
checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public :
68396840
ModifierFlags.Protected;
@@ -7262,9 +7263,16 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) {
72627263
this.escapedName = name;
72637264
this.declarations = undefined;
72647265
this.valueDeclaration = undefined;
7265-
this.id = undefined;
7266-
this.mergeId = undefined;
7266+
this.id = 0;
7267+
this.mergeId = 0;
72677268
this.parent = undefined;
7269+
this.members = undefined;
7270+
this.exports = undefined;
7271+
this.exportSymbol = undefined;
7272+
this.constEnumOnlyModule = undefined;
7273+
this.isReferenced = undefined;
7274+
this.isAssigned = undefined;
7275+
(this as any).links = undefined; // used by TransientSymbol
72687276
}
72697277

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

src/services/codefixes/fixInvalidImportSyntax.ts

+3-3
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

+6-6
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

+1-1
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

+1-1
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

+7-4
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

+3-3
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

+4-3
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));

src/services/utilities.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ import {
225225
isTaggedTemplateExpression,
226226
isTemplateLiteralKind,
227227
isToken,
228+
isTransientSymbol,
228229
isTypeAliasDeclaration,
229230
isTypeElement,
230231
isTypeNode,
@@ -336,7 +337,6 @@ import {
336337
textSpanEnd,
337338
Token,
338339
tokenToString,
339-
TransientSymbol,
340340
tryCast,
341341
Type,
342342
TypeChecker,
@@ -3003,9 +3003,9 @@ export function getScriptKind(fileName: string, host: LanguageServiceHost): Scri
30033003
/** @internal */
30043004
export function getSymbolTarget(symbol: Symbol, checker: TypeChecker): Symbol {
30053005
let next: Symbol = symbol;
3006-
while (isAliasSymbol(next) || (isTransientSymbol(next) && next.target)) {
3007-
if (isTransientSymbol(next) && next.target) {
3008-
next = next.target;
3006+
while (isAliasSymbol(next) || (isTransientSymbol(next) && next.links.target)) {
3007+
if (isTransientSymbol(next) && next.links.target) {
3008+
next = next.links.target;
30093009
}
30103010
else {
30113011
next = skipAlias(next, checker);
@@ -3014,10 +3014,6 @@ export function getSymbolTarget(symbol: Symbol, checker: TypeChecker): Symbol {
30143014
return next;
30153015
}
30163016

3017-
function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
3018-
return (symbol.flags & SymbolFlags.Transient) !== 0;
3019-
}
3020-
30213017
function isAliasSymbol(symbol: Symbol): boolean {
30223018
return (symbol.flags & SymbolFlags.Alias) !== 0;
30233019
}

0 commit comments

Comments
 (0)