type-inference #39

Open
alex-eg wants to merge 17 commits from type-inference into main
5 changed files with 56 additions and 10 deletions
Showing only changes of commit f5eee71eb7 - Show all commits

View File

@@ -99,7 +99,7 @@ sextest:
SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \
Review

Re 351. One per line, alphabetical. e87463b.

Re 351. One per line, alphabetical. e87463b.
feature-flags lambdas compound-literals closures fixpoint \
wildcards inference type-shapes unnamed-params \
closure-signatures
closure-signatures c99
# Multi-module linking is checked end to end; see tests/modules/Makefile.
check-modules: sexc

View File

@@ -509,10 +509,12 @@
((eq? a b) #t)
((unknown-type? a) #t)
((unknown-type? b) #t)
((and (tvar? a) (tvar? b) (tvar-rigid? b) (not (tvar-rigid? a)))
(bind-tvar! a b form))
((tvar? a) (bind-tvar! a b form))
((tvar? b) (bind-tvar! b a form))
;; Whichever side is free takes the binding, so that a rigid
;; variable is solved *to* rather than solved, in either order.
;; Both rigid and distinct is the mismatch `eq?' above let through.
((and (tvar? a) (not (tvar-rigid? a))) (bind-tvar! a b form))
((and (tvar? b) (not (tvar-rigid? b))) (bind-tvar! b a form))
((or (tvar? a) (tvar? b)) (type-mismatch a b form))
;; A typedef unifies as what it stands for. Its name survives in
;; whichever side is printed later, since neither side is rebuilt.
((alias-type? a) (unify (alias-expansion a) b form))

View File

@@ -693,13 +693,19 @@
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(define +closure-env-bytes+ 16)
;;; +closure-env-bytes+ for maximum capacity, max-align-t for
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
;;; effectiveness, hence union
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
;;; +closure-env-bytes+ for maximum capacity, and an alignment wide
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
;;; enough for anything that fits in them, hence union. `max_align_t'
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
;;; would say that in one word, but it is C11 and the target is C99, so
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
;;; the widest built-ins say it instead: a union is aligned for the
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
Review

Re 344. decay has its caller.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))   ->  int * p = a + 1;

e87463b.

Re 344. `decay` has its caller. ``` (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) -> int * p = a + 1; ``` e87463b.
;;; strictest of its members.
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(define +closure-env-type+ 'ƛenv)
(define (closure-env-declaration)
`(union ,+closure-env-type+ ((align max-align-t)
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(bytes (¤ char ,+closure-env-bytes+)))))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
`(union ,+closure-env-type+ ((bytes (¤ char ,+closure-env-bytes+))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(align-integer (long long))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(align-real (long double))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(align-pointer (* void))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(align-code (fn ((* void)) void)))))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
(define +closure-structs+ (make-hash-table))
(define +closure-forwards+ (make-hash-table))
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
Review

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.

With n a size-t (an unparsed name, rank 1, same as int):

int m1 = 1 + n;
size_t m2 = n + 1;

cc accepts both. C types 1 + n as size_t, and the int declaration narrows. The same tie makes (+ i u) an int and (+ u i) an unsigned int. char + char is declared char.

size-t never reaches the long rank because it is not a typedef Sex has parsed, so the strlen example's (+ 1 n) lands here.

When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With `n` a `size-t` (an unparsed name, rank 1, same as `int`): ```c int m1 = 1 + n; size_t m2 = n + 1; ``` `cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`. `size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
Review

The forms that reach this walk are &&, ||, |, &, <<, >>, ++. c-and and c-or are the names from before those spellings. The writer already emits a && b from (&& a b).

(var ok _ (&& a b)) and (var x _ (<< a b)) and (var x _ (++ a)) all stop with cannot infer the type of.

The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`. `(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
Review

An array operand is returned as the array. decay in infer.scm is the conversion this comment describes, and nothing in the walk calls it.

(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))

emits int p[4] = a + 1. cc: invalid initializer.

An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it. ```sex (var a (¤ int 4) #(1 2 3 4)) (var p _ (+ a 1)) ``` emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
Review

A subscript types only when the base's surface form is already (¤ ...). An (* int) is not, so

(fn f ((p (* int))) int
  (var e _ (¤ p 0))
  (return e))

stops with cannot infer the type of. This is the other use named on decay.

A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so ```sex (fn f ((p (* int))) int (var e _ (¤ p 0)) (return e)) ``` stops with `cannot infer the type of`. This is the other use named on `decay`.
Review

resolve-closure-types runs here and resolve-wildcard does not. (var n _ 1) at toplevel emits static _ n = 1. cc: unknown type name '_'. The same form inside a function becomes int n = 1.

`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
Review

walk-parts walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a fn type.

(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))

-m shows (var fp (fn ((ƛint_int_call c int)) int) ...), and the C is int (*fp)(c int). With no closure named c, the same declaration emits int (*fp)(int).

`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type. ```sex (var c (closure ((int)) int) (make-adder 1)) (var fp (fn ((c int)) int) (lambda ((c int)) int (return c))) ``` `-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
Review

The callee is typed before it is walked, so a closure literal has no type yet and falls through to convert-arguments. After the walk the form is a call of the constructor:

return ƛ0_f_make()(5);

cc: called object is not a function or function pointer. ((lambda ((x int)) int (return x)) 5) works, because the lifted function is registered before the call is typed. ((make-adder 20) 22) works, because the callee is already a call with a closure return type.

The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor: ```c return ƛ0_f_make()(5); ``` `cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.

View File

@@ -183,7 +183,17 @@
(test-assert "an ordinary variable binds to it instead"
(unify a r #f))
;; An unsolved variable resolves to itself.
(test-assert "it is still open" (tvar? (resolve r))))))
(test-assert "it is still open" (tvar? (resolve r))))
;; ...and the same the other way round: it is which side is free
;; that decides, not which side was written first.
(let ((r (fresh-rigid-tvar))
(a (fresh-tvar)))
(test-assert "rigid first binds the free one" (unify r a #f))
(test-assert "to the parameter itself" (eq? r (resolve a))))
(let ((r1 (fresh-rigid-tvar))
(r2 (fresh-rigid-tvar)))
(test-error "two parameters do not unify with each other"
(unify r1 r2 #f)))))
(test-group "constraints"
(test #t (entails? 'numeric (parse-type 'int)))

View File

@@ -0,0 +1,28 @@
(compilation "-- -std=c99 -pedantic-errors")
(input)
(output "c99: 42")
(return 0)
;;; The closure environment is part of the ABI, so its union is declared
;;; in every translation unit whether or not one is used. That put
;;; whatever it was written with into every program: `max_align_t' named
;;; the alignment in one word, and made C11 the floor for a program with
;;; no closure in it at all.
;;;
;;; The widest built-ins say the same thing -- a union is aligned for the
;;; strictest of its members -- and say it in C99.
;;;
;;; Closures themselves still want C11 for the `_Static_assert' that
;;; checks the captures fit, so this program keeps clear of them.
(include stdio.h)
(struct point ((x int) (y int)))
(fn area ((p (struct point))) int
(return (* (. p x) (. p y))))
(pub fn main () int
(var p (struct point) #((struct point) : 6 7))
(printf "c99: %d\n" (area p))
(return 0))