LINUX.ORG.RU

История изменений

Исправление quasimoto, (текущая версия) :

Но выглядит тормозным (это оно машинный код на стеке пишет во время выполнения) и неудобным (максимум один nest).

Поэтому на уровне фронтенда и рантаймовых фишек (чтобы не говорить «VM»). Например:

f(x, y) = x + y
g(x) = \y -> f x y

будет

struct f_int_int_int_capture_first { int x; int(&f)(int, int); };
int call(f_int_int_int_capture_first &fn, int y) { return fn.f(fn.x, y); }
int f(int x, int y) { return x + y; }
f_int_int_int_capture_first& g(int x) { return *new /* gc_new */ f_int_int_int_capture_first{x, f}; }
int main() { return call(g(1), 2); }

В фронтенде всё знаем — про типы и захваты, так что аллоцируем такие структуры и сами расставляем call, в IR для этого всё есть. Типичная проблема с необходимостью счётчика ссылок или GC для замыканий — тоже имеет место быть. Ну и разные агрессивные оптимизации к ним применимы, как в C++ или в Haskell.

Исходная версия quasimoto, :

Но выглядит тормозным (это оно машинный код на стеке пишет во время выполнения) и неудобным (максимум один nest).

Поэтому на уровне фронтенда и рантаймовых фишек (чтобы не говорить «VM»). Например:

f(x, y) = x + y
g(x) = \y -> f x y

будет

struct f_int_int_int_capture_first { int x; int(&f)(int, int); };
int call(f_int_int_int_capture_first &fn, int y) { return fn.f(fn.x, y); }
int f(int x, int y) { return x + y; }
f_int_int_int_capture_first& g(int x) { return *new /* gc_new */ f_int_int_int_capture_first{x, f}; }
int main() { return call(g(1), 2); }
➜  ~  g++ --std=c++11 -O2 closure.cc; ./a.out; echo $?
3
➜  ~  g++ --std=c++11 -O2 -S closure.cc; cat closure.s
	.file	"q.cc"
	.text
	.p2align 4,,15
	.globl	_Z1fii
	.type	_Z1fii, @function
_Z1fii:
.LFB1:
	.cfi_startproc
	leal	(%rdi,%rsi), %eax
	ret
	.cfi_endproc
.LFE1:
	.size	_Z1fii, .-_Z1fii
	.p2align 4,,15
	.globl	_Z4callR27f_int_int_int_capture_firsti
	.type	_Z4callR27f_int_int_int_capture_firsti, @function
_Z4callR27f_int_int_int_capture_firsti:
.LFB0:
	.cfi_startproc
	movq	8(%rdi), %rax
	movl	(%rdi), %edi
	jmp	*%rax
	.cfi_endproc
.LFE0:
	.size	_Z4callR27f_int_int_int_capture_firsti, .-_Z4callR27f_int_int_int_capture_firsti
	.p2align 4,,15
	.globl	_Z1gi
	.type	_Z1gi, @function
_Z1gi:
.LFB2:
	.cfi_startproc
	pushq	%rbx
	.cfi_def_cfa_offset 16
	.cfi_offset 3, -16
	movl	%edi, %ebx
	movl	$16, %edi
	call	_Znwm
	movl	%ebx, (%rax)
	movq	$_Z1fii, 8(%rax)
	popq	%rbx
	.cfi_def_cfa_offset 8
	ret
	.cfi_endproc
.LFE2:
	.size	_Z1gi, .-_Z1gi
	.section	.text.startup,"ax",@progbits
	.p2align 4,,15
	.globl	main
	.type	main, @function
main:
.LFB3:
	.cfi_startproc
	subq	$8, %rsp
	.cfi_def_cfa_offset 16
	movl	$16, %edi
	call	_Znwm
	movl	$1, (%rax)
	movq	$_Z1fii, 8(%rax)
	movl	$3, %eax
	addq	$8, %rsp
	.cfi_def_cfa_offset 8
	ret

В фронтенде всё знаем — про типы и захваты, так что аллоцируем такие структуры и сами расставляем call, в IR для этого всё есть. Типичная проблема с необходимостью счётчика ссылок или GC для замыканий — тоже имеет место быть. Ну и разные агрессивные оптимизации к ним применимы, как в C++ или в Haskell.