type-inference #39
2
Makefile
@@ -99,7 +99,7 @@ sextest:
|
||||
SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \
|
||||
|
|
||||
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
|
||||
|
||||
10
infer.scm
@@ -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))
|
||||
|
||||
14
semen.scm
@@ -693,13 +693,19 @@
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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.
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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'
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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.
alex-eg
commented
Re 344. 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.
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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)
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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+)))))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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+))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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)))))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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))
|
||||
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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.
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
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.
pkulev
commented
The forms that reach this walk are
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`.
pkulev
commented
An array operand is returned as the array. emits 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`.
pkulev
commented
A subscript types only when the base's surface form is already stops with 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`.
pkulev
commented
`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`.
pkulev
commented
`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)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
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.
|
||||
@@ -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)))
|
||||
|
||||
28
tests/sex-programs/c99.sex
Normal 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))
|
||||
Re 351. One per line, alphabetical.
e87463b.