Как работает компиляция круговых зависимостей?

Я привел пример на Java, но я думаю (не проверен), что он работает на других языках (все?).

У вас есть 2 файла. Сначала M.java:

public class MType {
    XType x;
    MType() {x = null;}
}

Во-вторых, другой файл (в том же каталоге), XType.java:

public class XType {
   MType m;
   public XType(MType m) {this.m = m;}
}

Хорошо, это плохое программирование, но если вы запустите javac XType, он скомпилирует: компилирует даже MType, потому что XType нуждается в нем. Но... MType требуется XType... как это работает? Как компилятор знает, что происходит?

Я хотел бы знать, как компилятор (javac или любые другие компиляторы, которых вы знаете) управляет этой ситуацией, а не как его избежать.

Я спрашиваю, потому что я пишу прекомпилятор, и я хотел бы управлять этой ситуацией.

Ответы

Ответ 1

Вам нужно выполнить двухпроходный или multi-pass:

Языки, такие как Java, требуют многопроходного компилятора, так как определение x не требуется, прежде чем использовать:

public class Example {  
public static void main(String [] args) {
    assert(x==0);           
    x++;
    assert(x==1);
}
static int x=0;
}

Существуют различные подходы, например, вы можете сделать следующее:

Первый проход может искать все объявления переменных, второй для объявлений методов и т.д., пока последний проход не использует всю эту информацию для компиляции окончательного кода.

Ответ 2

Первый файл не должен ничего знать о XType, кроме того, что это тип, и аналогично для MType во втором файле. Кроме того, на Java все объекты имеют одинаковый размер (потому что к нему обращаются все ссылки), поэтому размер объекта не требуется. Это не так на других языках - ваш код в его нынешнем виде не будет компилироваться в С++, например (синтаксис языка отдельно).