@@ -1129,7 +1129,7 @@ export class Program extends DiagnosticEmitter {
1129
1129
break;
1130
1130
}
1131
1131
case NodeKind.InterfaceDeclaration: {
1132
- this.initializeInterface(<InterfaceDeclaration>statement, file, queuedExtends );
1132
+ this.initializeInterface(<InterfaceDeclaration>statement, file, queuedImplements );
1133
1133
break;
1134
1134
}
1135
1135
case NodeKind.NamespaceDeclaration: {
@@ -1302,64 +1302,45 @@ export class Program extends DiagnosticEmitter {
1302
1302
}
1303
1303
}
1304
1304
1305
- // resolve prototypes of extended classes or interfaces
1305
+ // resolve prototypes of extended classes
1306
1306
let resolver = this.resolver;
1307
1307
for (let i = 0, k = queuedExtends.length; i < k; ++i) {
1308
1308
let thisPrototype = queuedExtends[i];
1309
+ assert(thisPrototype.kind == ElementKind.ClassPrototype);
1309
1310
let extendsNode = assert(thisPrototype.extendsNode); // must be present if in queuedExtends
1310
1311
let baseElement = resolver.resolveTypeName(extendsNode.name, thisPrototype.parent);
1311
1312
if (!baseElement) continue;
1312
- if (thisPrototype.kind == ElementKind.ClassPrototype) {
1313
- if (baseElement.kind == ElementKind.ClassPrototype) {
1314
- let basePrototype = <ClassPrototype>baseElement;
1315
- if (basePrototype.hasDecorator(DecoratorFlags.Final)) {
1316
- this.error(
1317
- DiagnosticCode.Class_0_is_final_and_cannot_be_extended,
1318
- extendsNode.range, basePrototype.identifierNode.text
1319
- );
1320
- }
1321
- if (
1322
- basePrototype.hasDecorator(DecoratorFlags.Unmanaged) !=
1323
- thisPrototype.hasDecorator(DecoratorFlags.Unmanaged)
1324
- ) {
1325
- this.error(
1326
- DiagnosticCode.Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa,
1327
- Range.join(thisPrototype.identifierNode.range, extendsNode.range)
1328
- );
1329
- }
1330
- if (!thisPrototype.extends(basePrototype)) {
1331
- thisPrototype.basePrototype = basePrototype;
1332
- } else {
1333
- this.error(
1334
- DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression,
1335
- basePrototype.identifierNode.range,
1336
- basePrototype.identifierNode.text,
1337
- );
1338
- }
1339
- } else {
1313
+ if (baseElement.kind == ElementKind.ClassPrototype) {
1314
+ let basePrototype = <ClassPrototype>baseElement;
1315
+ if (basePrototype.hasDecorator(DecoratorFlags.Final)) {
1340
1316
this.error(
1341
- DiagnosticCode.A_class_may_only_extend_another_class ,
1342
- extendsNode.range
1317
+ DiagnosticCode.Class_0_is_final_and_cannot_be_extended ,
1318
+ extendsNode.range, basePrototype.identifierNode.text
1343
1319
);
1344
1320
}
1345
- } else if (thisPrototype.kind == ElementKind.InterfacePrototype) {
1346
- if (baseElement.kind == ElementKind.InterfacePrototype) {
1347
- const basePrototype = <InterfacePrototype>baseElement;
1348
- if (!thisPrototype.extends(basePrototype)) {
1349
- thisPrototype.basePrototype = basePrototype;
1350
- } else {
1351
- this.error(
1352
- DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression,
1353
- basePrototype.identifierNode.range,
1354
- basePrototype.identifierNode.text,
1355
- );
1356
- }
1321
+ if (
1322
+ basePrototype.hasDecorator(DecoratorFlags.Unmanaged) !=
1323
+ thisPrototype.hasDecorator(DecoratorFlags.Unmanaged)
1324
+ ) {
1325
+ this.error(
1326
+ DiagnosticCode.Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa,
1327
+ Range.join(thisPrototype.identifierNode.range, extendsNode.range)
1328
+ );
1329
+ }
1330
+ if (!thisPrototype.extends(basePrototype)) {
1331
+ thisPrototype.basePrototype = basePrototype;
1357
1332
} else {
1358
1333
this.error(
1359
- DiagnosticCode.An_interface_can_only_extend_an_interface,
1360
- extendsNode.range
1334
+ DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression,
1335
+ basePrototype.identifierNode.range,
1336
+ basePrototype.identifierNode.text,
1361
1337
);
1362
1338
}
1339
+ } else {
1340
+ this.error(
1341
+ DiagnosticCode.A_class_may_only_extend_another_class,
1342
+ extendsNode.range
1343
+ );
1363
1344
}
1364
1345
}
1365
1346
@@ -1398,7 +1379,7 @@ export class Program extends DiagnosticEmitter {
1398
1379
}
1399
1380
}
1400
1381
1401
- // resolve prototypes of implemented interfaces
1382
+ // resolve prototypes of implemented/extended interfaces
1402
1383
for (let i = 0, k = queuedImplements.length; i < k; ++i) {
1403
1384
let thisPrototype = queuedImplements[i];
1404
1385
let implementsNodes = assert(thisPrototype.implementsNodes); // must be present if in queuedImplements
@@ -1410,10 +1391,23 @@ export class Program extends DiagnosticEmitter {
1410
1391
let interfacePrototype = <InterfacePrototype>interfaceElement;
1411
1392
let interfacePrototypes = thisPrototype.interfacePrototypes;
1412
1393
if (!interfacePrototypes) thisPrototype.interfacePrototypes = interfacePrototypes = new Array();
1413
- interfacePrototypes.push(interfacePrototype);
1394
+ if (
1395
+ thisPrototype.kind == ElementKind.Interface &&
1396
+ thisPrototype.implements(interfacePrototype)
1397
+ ) {
1398
+ this.error(
1399
+ DiagnosticCode._0_is_referenced_directly_or_indirectly_in_its_own_base_expression,
1400
+ interfacePrototype.identifierNode.range,
1401
+ interfacePrototype.identifierNode.text,
1402
+ );
1403
+ } else {
1404
+ interfacePrototypes.push(interfacePrototype);
1405
+ }
1414
1406
} else {
1415
1407
this.error(
1416
- DiagnosticCode.A_class_can_only_implement_an_interface,
1408
+ thisPrototype.kind == ElementKind.InterfacePrototype
1409
+ ? DiagnosticCode.An_interface_can_only_extend_an_interface
1410
+ : DiagnosticCode.A_class_can_only_implement_an_interface,
1417
1411
implementsNode.range
1418
1412
);
1419
1413
}
@@ -2473,7 +2467,7 @@ export class Program extends DiagnosticEmitter {
2473
2467
break;
2474
2468
}
2475
2469
case NodeKind.InterfaceDeclaration: {
2476
- element = this.initializeInterface(<InterfaceDeclaration>declaration, parent, queuedExtends );
2470
+ element = this.initializeInterface(<InterfaceDeclaration>declaration, parent, queuedImplements );
2477
2471
break;
2478
2472
}
2479
2473
case NodeKind.NamespaceDeclaration: {
@@ -2624,7 +2618,7 @@ export class Program extends DiagnosticEmitter {
2624
2618
/** Parent element, usually a file or namespace. */
2625
2619
parent: Element,
2626
2620
/** So far queued `extends` clauses. */
2627
- queuedExtends : ClassPrototype[],
2621
+ queuedImplements : ClassPrototype[],
2628
2622
): InterfacePrototype | null {
2629
2623
let name = declaration.name.text;
2630
2624
let element = new InterfacePrototype(
@@ -2637,8 +2631,10 @@ export class Program extends DiagnosticEmitter {
2637
2631
);
2638
2632
if (!parent.add(name, element)) return null;
2639
2633
2640
- // remember interfaces that extend another interface
2641
- if (declaration.extendsType) queuedExtends.push(element);
2634
+ // remember interfaces that extend other interfaces
2635
+ // Note: See the corresponding note in parseClassOrInterface (in parser.ts) for
2636
+ // further information as to why implementsTypes is used.
2637
+ if (declaration.implementsTypes) queuedImplements.push(element);
2642
2638
2643
2639
let memberDeclarations = declaration.members;
2644
2640
for (let i = 0, k = memberDeclarations.length; i < k; ++i) {
@@ -2757,7 +2753,7 @@ export class Program extends DiagnosticEmitter {
2757
2753
break;
2758
2754
}
2759
2755
case NodeKind.InterfaceDeclaration: {
2760
- this.initializeInterface(<InterfaceDeclaration>member, original, queuedExtends );
2756
+ this.initializeInterface(<InterfaceDeclaration>member, original, queuedImplements );
2761
2757
break;
2762
2758
}
2763
2759
case NodeKind.NamespaceDeclaration: {
@@ -4270,6 +4266,24 @@ export class ClassPrototype extends DeclaredElement {
4270
4266
return false;
4271
4267
}
4272
4268
4269
+ implements(other: InterfacePrototype, seen: Set<InterfacePrototype> | null = null): bool {
4270
+ if (this.interfacePrototypes) {
4271
+ if (!seen) seen = new Set();
4272
+ let interfacePrototypes = assert(this.interfacePrototypes);
4273
+
4274
+ for (let i = 0, k = interfacePrototypes.length; i < k; ++i) {
4275
+ let prototype = unchecked(interfacePrototypes[i]);
4276
+
4277
+ if (prototype == other) return true;
4278
+ if (seen.has(prototype)) continue;
4279
+ seen.add(prototype);
4280
+
4281
+ if (prototype.implements(other, seen)) return true;
4282
+ }
4283
+ }
4284
+ return false;
4285
+ }
4286
+
4273
4287
/** Adds an element as an instance member of this one. Returns the previous element if a duplicate. */
4274
4288
addInstance(name: string, element: DeclaredElement): bool {
4275
4289
let originalDeclaration = element.declaration;
@@ -4529,9 +4543,11 @@ export class Class extends TypedElement {
4529
4543
// Start with the interface itself, adding this class and its extenders to
4530
4544
// its implementers. Repeat for the interface's bases that are indirectly
4531
4545
// implemented by means of being extended by the interface.
4532
- let nextIface: Interface | null = iface;
4546
+ // TODO: Maybe add a fast path when `iface` has no bases?
4547
+ let ifaceStack = [iface];
4533
4548
let extenders = this.extenders;
4534
4549
do {
4550
+ let nextIface = assert(ifaceStack.pop());
4535
4551
let implementers = nextIface.implementers;
4536
4552
if (!implementers) nextIface.implementers = implementers = new Set();
4537
4553
implementers.add(this);
@@ -4541,8 +4557,19 @@ export class Class extends TypedElement {
4541
4557
implementers.add(extender);
4542
4558
}
4543
4559
}
4544
- nextIface = <Interface | null>nextIface.base;
4545
- } while (nextIface);
4560
+
4561
+ let nextIfaces = nextIface.interfaces;
4562
+ if (!nextIfaces) continue;
4563
+
4564
+ let stackIndex = ifaceStack.length;
4565
+
4566
+ // Calls the internal ensureCapacity() when run in the bootstrapped compiler:
4567
+ ifaceStack.length = stackIndex + nextIfaces.size;
4568
+
4569
+ for (let _values = Set_values(nextIfaces), i = 0, k = _values.length; i < k; ++i) {
4570
+ ifaceStack[stackIndex++] = unchecked(_values[i]);
4571
+ }
4572
+ } while (ifaceStack.length);
4546
4573
}
4547
4574
4548
4575
/** Adds an interface. */
@@ -4561,7 +4588,7 @@ export class Class extends TypedElement {
4561
4588
if (target.isInterface) {
4562
4589
if (this.isInterface) {
4563
4590
// targetInterface = thisInterface
4564
- return this == target || this.extends( target);
4591
+ return this == target || this.implements(<Interface> target);
4565
4592
} else {
4566
4593
// targetInterface = thisClass
4567
4594
return this.implements(<Interface>target);
@@ -4835,7 +4862,7 @@ export class Class extends TypedElement {
4835
4862
return true;
4836
4863
}
4837
4864
4838
- /** Tests if this class or interface extends the given class or interface . */
4865
+ /** Tests if this class extends the given class. */
4839
4866
extends(other: Class): bool {
4840
4867
return other.hasExtender(this);
4841
4868
}
0 commit comments