@@ -2729,6 +2729,9 @@ export class Resolver extends DiagnosticEmitter {
27292729 ) ;
27302730 }
27312731
2732+ /** Currently resolving classes. */
2733+ private resolveClassPending : Class [ ] = [ ] ;
2734+
27322735 /** Resolves a class prototype using the specified concrete type arguments. */
27332736 resolveClass (
27342737 /** The prototype of the class. */
@@ -2742,10 +2745,19 @@ export class Resolver extends DiagnosticEmitter {
27422745 ) : Class | null {
27432746 var instanceKey = typeArguments ? typesToString ( typeArguments ) : "" ;
27442747
2745- // Check if this exact instance has already been resolved
2748+ // Do not attempt to resolve the same class twice. This can return a class
2749+ // that isn't fully resolved yet, but only on deeper levels of recursion.
27462750 var instance = prototype . getResolvedInstance ( instanceKey ) ;
27472751 if ( instance ) return instance ;
27482752
2753+ // Otherwise create
2754+ var nameInclTypeParamters = prototype . name ;
2755+ if ( instanceKey . length ) nameInclTypeParamters += "<" + instanceKey + ">" ;
2756+ instance = new Class ( nameInclTypeParamters , prototype , typeArguments ) ;
2757+ prototype . setResolvedInstance ( instanceKey , instance ) ;
2758+ var pendingClasses = this . resolveClassPending ;
2759+ pendingClasses . push ( instance ) ;
2760+
27492761 // Insert contextual type arguments for this operation. Internally, this method is always
27502762 // called with matching type parameter / argument counts.
27512763 if ( typeArguments ) {
@@ -2760,13 +2772,10 @@ export class Resolver extends DiagnosticEmitter {
27602772 let typeParameterNodes = prototype . typeParameterNodes ;
27612773 assert ( ! ( typeParameterNodes && typeParameterNodes . length ) ) ;
27622774 }
2763-
2764- // There shouldn't be an instance before resolving the base class
2765- assert ( ! prototype . getResolvedInstance ( instanceKey ) ) ;
2775+ instance . contextualTypeArguments = ctxTypes ;
27662776
27672777 // Resolve base class if applicable
27682778 var basePrototype = prototype . basePrototype ;
2769- var baseClass : Class | null = null ;
27702779 if ( basePrototype ) {
27712780 let current : ClassPrototype | null = basePrototype ;
27722781 do {
@@ -2780,70 +2789,68 @@ export class Resolver extends DiagnosticEmitter {
27802789 }
27812790 } while ( current = current . basePrototype ) ;
27822791 let extendsNode = assert ( prototype . extendsNode ) ; // must be present if it has a base prototype
2783- baseClass = this . resolveClassInclTypeArguments (
2792+ let base = this . resolveClassInclTypeArguments (
27842793 basePrototype ,
27852794 extendsNode . typeArguments ,
27862795 prototype . parent , // relative to derived class
27872796 makeMap ( ctxTypes ) , // don't inherit
27882797 extendsNode ,
27892798 reportMode
27902799 ) ;
2791- if ( ! baseClass ) return null ;
2792- }
2800+ if ( ! base ) return null ;
2801+ instance . setBase ( base ) ;
27932802
2794- // Construct the instance and remember that it has been resolved already
2795- var recursiveInstanceFromResolvingBase = prototype . getResolvedInstance ( instanceKey ) ;
2796- if ( recursiveInstanceFromResolvingBase ) {
2797- // Happens if the base class already triggers resolving this class
2798- instance = recursiveInstanceFromResolvingBase ;
2799- } else {
2800- let nameInclTypeParamters = prototype . name ;
2801- if ( instanceKey . length ) nameInclTypeParamters += "<" + instanceKey + ">" ;
2802- instance = new Class ( nameInclTypeParamters , prototype , typeArguments , baseClass ) ;
2803- prototype . setResolvedInstance ( instanceKey , instance ) ;
2803+ // If the base class is still pending, yield here and instead resolve any
2804+ // derived classes once the base class's `finishResolveClass` is done.
2805+ // This is guaranteed to never happen at the entry of the recursion, i.e.
2806+ // where `resolveClass` is called from other code.
2807+ if ( pendingClasses . includes ( base ) ) return instance ;
28042808 }
2805- instance . contextualTypeArguments = ctxTypes ; // unique (as specified by the caller)
28062809
2807- // Inherit base class members and set up the initial memory offset for own fields
2810+ // We only get here if the base class has been fully resolved already.
2811+ this . finishResolveClass ( instance , reportMode ) ;
2812+ return instance ;
2813+ }
2814+
2815+ /** Finishes resolving the specified class. */
2816+ private finishResolveClass (
2817+ /** Class to finish resolving. */
2818+ instance : Class ,
2819+ /** How to proceed with eventual diagnostics. */
2820+ reportMode : ReportMode
2821+ ) : void {
2822+ var instanceMembers = instance . members ;
2823+ if ( ! instanceMembers ) instance . members = instanceMembers = new Map ( ) ;
2824+
2825+ // Alias base members
2826+ var pendingClasses = this . resolveClassPending ;
28082827 var memoryOffset : u32 = 0 ;
2809- if ( baseClass ) {
2810- let baseMembers = baseClass . members ;
2828+ var base = instance . base ;
2829+ if ( base ) {
2830+ assert ( ! pendingClasses . includes ( base ) ) ;
2831+ let baseMembers = base . members ;
28112832 if ( baseMembers ) {
2812- let instanceMembers = instance . members ;
2813- if ( ! instanceMembers ) instance . members = instanceMembers = new Map ( ) ;
28142833 for ( let [ baseMemberName , baseMember ] of baseMembers ) {
28152834 instanceMembers . set ( baseMemberName , baseMember ) ;
28162835 }
28172836 }
2818- memoryOffset = baseClass . currentMemoryOffset ;
2837+ memoryOffset = base . nextMemoryOffset ;
28192838 }
28202839
28212840 // Resolve instance members
2841+ var prototype = instance . prototype ;
28222842 var instanceMemberPrototypes = prototype . instanceMembers ;
28232843 if ( instanceMemberPrototypes ) {
28242844 for ( let member of instanceMemberPrototypes . values ( ) ) {
28252845 switch ( member . kind ) {
28262846
2827- // Lay out fields in advance
28282847 case ElementKind . FIELD_PROTOTYPE : {
2829- let instanceMembers = instance . members ;
2830- if ( ! instanceMembers ) instance . members = instanceMembers = new Map ( ) ;
2831- else if ( instanceMembers . has ( member . name ) ) {
2832- let existing = instanceMembers . get ( member . name ) ! ;
2833- this . errorRelated (
2834- DiagnosticCode . Duplicate_identifier_0 ,
2835- ( < FieldPrototype > member ) . identifierNode . range ,
2836- existing . declaration . name . range ,
2837- member . name
2838- ) ;
2839- break ;
2840- }
28412848 let fieldTypeNode = ( < FieldPrototype > member ) . typeNode ;
28422849 let fieldType : Type | null = null ;
28432850 // TODO: handle duplicate non-private fields specifically?
28442851 if ( ! fieldTypeNode ) {
2845- if ( baseClass ) {
2846- let baseMembers = baseClass . members ;
2852+ if ( base ) {
2853+ let baseMembers = base . members ;
28472854 if ( baseMembers && baseMembers . has ( ( < FieldPrototype > member ) . name ) ) {
28482855 let baseField = baseMembers . get ( ( < FieldPrototype > member ) . name ) ! ;
28492856 if ( ! baseField . is ( CommonFlags . PRIVATE ) ) {
@@ -2869,13 +2876,13 @@ export class Resolver extends DiagnosticEmitter {
28692876 ) ;
28702877 }
28712878 if ( ! fieldType ) break ; // did report above
2872- let fieldInstance = new Field ( < FieldPrototype > member , instance , fieldType ) ;
2879+ let field = new Field ( < FieldPrototype > member , instance , fieldType ) ;
28732880 assert ( isPowerOf2 ( fieldType . byteSize ) ) ;
28742881 let mask = fieldType . byteSize - 1 ;
28752882 if ( memoryOffset & mask ) memoryOffset = ( memoryOffset | mask ) + 1 ;
2876- fieldInstance . memoryOffset = memoryOffset ;
2883+ field . memoryOffset = memoryOffset ;
28772884 memoryOffset += fieldType . byteSize ;
2878- instance . add ( member . name , fieldInstance ) ; // reports
2885+ instance . add ( member . name , field ) ; // reports
28792886 break ;
28802887 }
28812888 case ElementKind . FUNCTION_PROTOTYPE : {
@@ -2923,7 +2930,7 @@ export class Resolver extends DiagnosticEmitter {
29232930 }
29242931
29252932 // Finalize memory offset
2926- instance . currentMemoryOffset = memoryOffset ;
2933+ instance . nextMemoryOffset = memoryOffset ;
29272934
29282935 // Link _own_ constructor if present
29292936 {
@@ -2933,7 +2940,7 @@ export class Resolver extends DiagnosticEmitter {
29332940 let ctorInstance = this . resolveFunction (
29342941 < FunctionPrototype > ctorPrototype ,
29352942 null ,
2936- instance . contextualTypeArguments ,
2943+ assert ( instance . contextualTypeArguments ) ,
29372944 reportMode
29382945 ) ;
29392946 if ( ctorInstance ) instance . constructorInstance = < Function > ctorInstance ;
@@ -3002,7 +3009,24 @@ export class Resolver extends DiagnosticEmitter {
30023009 }
30033010 }
30043011 }
3005- return instance ;
3012+
3013+ // Remove this class from pending
3014+ var pendingIndex = pendingClasses . indexOf ( instance ) ;
3015+ assert ( ~ pendingIndex ) ; // must be pending
3016+ pendingClasses . splice ( pendingIndex , 1 ) ;
3017+
3018+ // Finish derived classes that we postponed in `resolveClass` due to the
3019+ // base class still being pending, again triggering `finishResolveClass`
3020+ // of any classes derived from those classes, ultimately leading to all
3021+ // pending classes being resolved.
3022+ var derivedPendingClasses = new Array < Class > ( ) ;
3023+ for ( let i = 0 , k = pendingClasses . length ; i < k ; ++ i ) {
3024+ let pending = pendingClasses [ i ] ;
3025+ if ( instance == pending . base ) derivedPendingClasses . push ( pending ) ;
3026+ }
3027+ for ( let i = 0 , k = derivedPendingClasses . length ; i < k ; ++ i ) {
3028+ this . finishResolveClass ( derivedPendingClasses [ i ] , reportMode ) ;
3029+ }
30063030 }
30073031
30083032 /** Resolves a class prototype by first resolving the specified type arguments. */
0 commit comments