Collegamento del compilatore: come gestire i riferimenti circolari?

7

Attualmente sto scrivendo un compilatore per una nuova lingua e sto lottando con l'aspetto di collegamento dei nuovi tipi quando esiste un riferimento circolare.

Ho creato un albero delle dipendenze in modo da poter compilare i Tipi nell'ordine delle loro dipendenze che sembrava aver risolto il mio problema ... cioè fino a quando non ho trovato riferimenti circolari come nell'esempio seguente (pubblicato in c #):

public class A
{
  public A() {  var b = new B(); }
}

public class B
{
  public B() { var a = new A(); }
}

Il mio limite è che posso creare solo 1 Tipo alla volta e, nel caso di riferimenti circolari, o A deve essere creato per primo, o B deve essere creato per primo, entrambi i quali rappresentano il problema che posso creare o a meno che non vengano create prima le dipendenze!

Come fanno i compilatori a superare questo?

    
posta OnResolve 25.07.2012 - 15:45
fonte

3 risposte

8

In generale, si crea A e lo si aggiunge all'ambito. Quando risolvi B, vai a costruire B. Quando questo risolve A, trova un tipo (parzialmente) costruito e afferra quel riferimento. Quindi si ritorna all'edificio A.

Ci sono altre situazioni che sono più problematiche (come l'ereditarietà ciclica, i riferimenti alle DLL, le dichiarazioni di tipo ricorsive) che spesso richiedono tipi di segnaposto o semplicemente non sono fattibili dati altri vincoli di progettazione. In generale, la riduzione di ogni fase della compilazione aiuta a prevenire problemi. Ad esempio, puoi eseguire il trucco di tipo parziale solo se non è necessario conoscere i membri di A in B.

    
risposta data 25.07.2012 - 16:00
fonte
4

Penso che stai cercando di risolvere nel compilatore qualcosa che dovrebbe essere risolto dal linker. Sia A che B dovrebbero compilare qualsiasi codice intermedio che usano (tradizionalmente un file oggetto) e solo elencare l'altro tipo come un simbolo esterno. Quando si genera il modulo eseguibile, si passa attraverso la tabella dei simboli per ciascun file oggetto e si risolvono i riferimenti esterni. Vedi questo per una buona introduzione ai linker tradizionali.

    
risposta data 25.07.2012 - 16:18
fonte
3

How do compilers overcome this?

Alcune lingue (C ++, Objective-C) consentono dichiarazioni in avanti che introducono il tipo ma non lo definiscono. Sarebbe qualcosa di simile:

public class B;  // now the compiler knows that B is a class that will be defined later

public class A
{
  public A() {  var b = new B(); }
}

public class B
{
  public B() { var a = new A(); }
}

Detto questo, anche una dichiarazione anticipata non risolverà il problema circolare nel tuo esempio: la creazione di una nuova A creerà una nuova B, che a sua volta creerà un'altra A, che creerà un'altra B, e così via. Se vuoi un A e un B che puntano l'un l'altro, dovrai creare costruttori che prendono un riferimento a un oggetto esistente, come:

public class A
{
  public A(B _b) { var b = _b; }
  public A()     { var b = new B(this); }  // 'this' is a C++-style pointer to the current object
}
    
risposta data 25.07.2012 - 16:34
fonte

Leggi altre domande sui tag