function outer(i: integer): integer;
function inner(j: integer): integer;
function inner2(k: integer): integer;
begin
result:=i+j+k+1;
end;
begin
result := i+j+inner2(i);
end;
begin
i:=1;
result := i+inner(i);
end;
Внутренняя функция должна иметь какой-то доступ к параметрам внешней. А так как параметры внешней расположены на стеке, то они не имеют фиксированного адреса (т.к. может быть вызов f-g-h или f-h, и внутри h переенные могут быть расположены как сразу за переменными f, так и после переменных g). И их адрес даже не обязательно фиксирован относитель начала стека на момент вызова внутренней функции.
Поэтому внутренняя функция имеет потайной параметр - указатель на вершину стека на момент вызова внешней функции. Через него и происходит адресация к переменным внешней функции.
Кстати, именно из-за этого локальные функции в Д7 нельзя передавать никуда по указателю. В других языках либо введено понятие "ссылка на функцию", которые хранит указатель на допольнительные данные, либо вообще все указатели хранят доп.информацию, либо ещё как-то выкручиваются, не знаю, как.
Но мне стало интересно, а как происходит работа с внутренней внутренней функцией. Передаются ли в неё два параметра (на вершины стеков внешних функций), или как-то ещё.
Оказалось, что передаётся один параметр - указатель на локальные переменные первой внутренней функции. Чтобы получить доступ к внешним локальным переменным, делается двойное разыменование: сначала разываменовываем указатель на переменные первой внутренней, среди них находим указатель на переменные внешней и уже его разыменовываем.
Эффективность такого подхода меня очень пугает. Почему был выбран именно такой вариант вместо передачи двух указателей?
Однако компилятор Д7 таки проводит оптимизации! Если внутренняя функция не обращается к переменым внешней, то в неё не передается указатель на стек. Увы, это единственная замеченная мной оптимизация.
Можно было сделать и больше оптимизаций.
Например, если внутренняя функция не вызывается ни в каких других внутренних функциях, то для неё вершина стека фиксирована относительно вершины стека внешней, то есть указатель передавать не надо. Этой оптимизации в Д7 нету.
Или, например, если используются только несколько параметров внешней (не более двух), то передать по ссылке только их, а не весь блок локальных переменных (тут надо понимать, что "передача по ссылке" - это не обязательно передача указателя, это может быть и передача самого значения с копированием обратно, причём передача хоть в регистре, что уберёт ненужную возню с разыменованиями и двойными разыменованиями).
Ну и про локальные лямбды, передаваемые наружу, у меня тоже есть некоторые вопросы, но об этом я спрошу в следующий раз.