@@ -1129,7 +1129,7 @@ export class Program extends DiagnosticEmitter {
11291129 break;
11301130 }
11311131 case NodeKind.InterfaceDeclaration: {
1132- this.initializeInterface(<InterfaceDeclaration>statement, file, queuedExtends );
1132+ this.initializeInterface(<InterfaceDeclaration>statement, file, queuedImplements );
11331133 break;
11341134 }
11351135 case NodeKind.NamespaceDeclaration: {
@@ -1302,64 +1302,45 @@ export class Program extends DiagnosticEmitter {
13021302 }
13031303 }
13041304
1305- // resolve prototypes of extended classes or interfaces
1305+ // resolve prototypes of extended classes
13061306 let resolver = this.resolver;
13071307 for (let i = 0, k = queuedExtends.length; i < k; ++i) {
13081308 let thisPrototype = queuedExtends[i];
1309+ assert(thisPrototype.kind == ElementKind.ClassPrototype);
13091310 let extendsNode = assert(thisPrototype.extendsNode); // must be present if in queuedExtends
13101311 let baseElement = resolver.resolveTypeName(extendsNode.name, thisPrototype.parent);
13111312 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)) {
13401316 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
13431319 );
13441320 }
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;
13571332 } else {
13581333 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,
13611337 );
13621338 }
1339+ } else {
1340+ this.error(
1341+ DiagnosticCode.A_class_may_only_extend_another_class,
1342+ extendsNode.range
1343+ );
13631344 }
13641345 }
13651346
@@ -1398,7 +1379,7 @@ export class Program extends DiagnosticEmitter {
13981379 }
13991380 }
14001381
1401- // resolve prototypes of implemented interfaces
1382+ // resolve prototypes of implemented/extended interfaces
14021383 for (let i = 0, k = queuedImplements.length; i < k; ++i) {
14031384 let thisPrototype = queuedImplements[i];
14041385 let implementsNodes = assert(thisPrototype.implementsNodes); // must be present if in queuedImplements
@@ -1410,10 +1391,23 @@ export class Program extends DiagnosticEmitter {
14101391 let interfacePrototype = <InterfacePrototype>interfaceElement;
14111392 let interfacePrototypes = thisPrototype.interfacePrototypes;
14121393 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+ }
14141406 } else {
14151407 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,
14171411 implementsNode.range
14181412 );
14191413 }
@@ -2473,7 +2467,7 @@ export class Program extends DiagnosticEmitter {
24732467 break;
24742468 }
24752469 case NodeKind.InterfaceDeclaration: {
2476- element = this.initializeInterface(<InterfaceDeclaration>declaration, parent, queuedExtends );
2470+ element = this.initializeInterface(<InterfaceDeclaration>declaration, parent, queuedImplements );
24772471 break;
24782472 }
24792473 case NodeKind.NamespaceDeclaration: {
@@ -2624,7 +2618,7 @@ export class Program extends DiagnosticEmitter {
26242618 /** Parent element, usually a file or namespace. */
26252619 parent: Element,
26262620 /** So far queued `extends` clauses. */
2627- queuedExtends : ClassPrototype[],
2621+ queuedImplements : ClassPrototype[],
26282622 ): InterfacePrototype | null {
26292623 let name = declaration.name.text;
26302624 let element = new InterfacePrototype(
@@ -2637,8 +2631,10 @@ export class Program extends DiagnosticEmitter {
26372631 );
26382632 if (!parent.add(name, element)) return null;
26392633
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);
26422638
26432639 let memberDeclarations = declaration.members;
26442640 for (let i = 0, k = memberDeclarations.length; i < k; ++i) {
@@ -2757,7 +2753,7 @@ export class Program extends DiagnosticEmitter {
27572753 break;
27582754 }
27592755 case NodeKind.InterfaceDeclaration: {
2760- this.initializeInterface(<InterfaceDeclaration>member, original, queuedExtends );
2756+ this.initializeInterface(<InterfaceDeclaration>member, original, queuedImplements );
27612757 break;
27622758 }
27632759 case NodeKind.NamespaceDeclaration: {
@@ -4270,6 +4266,24 @@ export class ClassPrototype extends DeclaredElement {
42704266 return false;
42714267 }
42724268
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+
42734287 /** Adds an element as an instance member of this one. Returns the previous element if a duplicate. */
42744288 addInstance(name: string, element: DeclaredElement): bool {
42754289 let originalDeclaration = element.declaration;
@@ -4529,9 +4543,11 @@ export class Class extends TypedElement {
45294543 // Start with the interface itself, adding this class and its extenders to
45304544 // its implementers. Repeat for the interface's bases that are indirectly
45314545 // 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];
45334548 let extenders = this.extenders;
45344549 do {
4550+ let nextIface = assert(ifaceStack.pop());
45354551 let implementers = nextIface.implementers;
45364552 if (!implementers) nextIface.implementers = implementers = new Set();
45374553 implementers.add(this);
@@ -4541,8 +4557,19 @@ export class Class extends TypedElement {
45414557 implementers.add(extender);
45424558 }
45434559 }
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);
45464573 }
45474574
45484575 /** Adds an interface. */
@@ -4561,7 +4588,7 @@ export class Class extends TypedElement {
45614588 if (target.isInterface) {
45624589 if (this.isInterface) {
45634590 // targetInterface = thisInterface
4564- return this == target || this.extends( target);
4591+ return this == target || this.implements(<Interface> target);
45654592 } else {
45664593 // targetInterface = thisClass
45674594 return this.implements(<Interface>target);
@@ -4835,7 +4862,7 @@ export class Class extends TypedElement {
48354862 return true;
48364863 }
48374864
4838- /** Tests if this class or interface extends the given class or interface . */
4865+ /** Tests if this class extends the given class. */
48394866 extends(other: Class): bool {
48404867 return other.hasExtender(this);
48414868 }
0 commit comments