diff --git a/Makefile b/Makefile index 3b4a8f4..664ff72 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,8 @@ sextest: SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \ feature-flags lambdas compound-literals closures fixpoint \ - wildcards inference type-shapes unnamed-params + wildcards inference type-shapes unnamed-params \ + closure-signatures # Multi-module linking is checked end to end; see tests/modules/Makefile. check-modules: sexc diff --git a/semen.scm b/semen.scm index 9ddbaf7..c547e25 100644 --- a/semen.scm +++ b/semen.scm @@ -765,10 +765,24 @@ *pending-closure-structs*))))) (delete-duplicates (aggregates-in type)))) +;;; Where one argument ends and the next begins has to survive the +;;; flattening, or `((long long))' and `((long) (long))' mangle alike and +;;; the second signature silently reuses the first one's struct. Words +;;; within an argument keep the single separator; the arguments take a +;;; doubled one. +;;; +;;; Not proof against a type name that mangles to a trailing `_' of its +;;; own -- for that the arguments would have to carry their lengths, and +;;; the name in the C is worth more than the last of the ambiguity. +(define (mangle-arglist args) + (if (null? args) + "void" + (string-intersperse (map mangle-type args) "__"))) + (define (closure-struct-name type) ;; the glyph says `closure' already, so the tag is just the signature (string->symbol (string-append "ƛ" - (mangle-type (second type)) + (mangle-arglist (second type)) "_" (mangle-type (third type))))) @@ -937,9 +951,9 @@ (let ((name (capture-name capture))) (unless (symbol? name) (sex-error form "a closure capture needs a name" capture)) - (let ((type (if (pair? capture) - (expression-type (capture-argument capture) env) - (lookup-name env name)))) + ;; the same lookup either way: a capture that borrows a name can + ;; borrow a global's or a function's, not only a local's + (let ((type (expression-type (capture-argument capture) env))) (unless type (sex-error form "cannot infer what is captured as" name)) (list name type)))) diff --git a/tests/sex-programs/closure-signatures.sex b/tests/sex-programs/closure-signatures.sex new file mode 100644 index 0000000..494bcde --- /dev/null +++ b/tests/sex-programs/closure-signatures.sex @@ -0,0 +1,52 @@ +(input) +(output "one argument of two words: 7" + "two arguments of one: 7" + "unsigned, one argument: 9" + "unsigned, two arguments: 3" + "captured global: 12" + "captured function: 8") +(return 0) + +;;; A closure's struct is named after its signature, so that two +;;; translation units agree on it without sharing a header. The name is +;;; built by flattening the argument list, and flattening loses where one +;;; argument ends and the next begins: `((long long))' and +;;; `((long) (long))' are different signatures that used to mangle alike, +;;; and the second quietly reused the first one's struct. +;;; +;;; A capture that borrows a name reads it from wherever the name is +;;; declared, a global or a function included. + +(include stdio.h) + +(var scale int 3) + +(fn double-it ((n int)) int + (return (* n 2))) + +(pub fn main () int + (var one-wide (closure ((long long)) int) + (closure ((a (long long))) int () (return (cast a int)))) + (printf "one argument of two words: %d\n" (one-wide 7)) + + (var two-longs (closure ((long) (long)) int) + (closure ((a long) (b long)) int () (return (cast (+ a b) int)))) + (printf "two arguments of one: %d\n" (two-longs 3 4)) + + (var one-unsigned (closure ((unsigned int)) int) + (closure ((a (unsigned int))) int () (return (cast a int)))) + (printf "unsigned, one argument: %d\n" (one-unsigned 9)) + + (var two-unsigned (closure ((unsigned) (int)) int) + (closure ((a unsigned) (b int)) int () (return (+ (cast a int) b)))) + (printf "unsigned, two arguments: %d\n" (two-unsigned 1 2)) + + ;; a capture names what it borrows, and the name need not be a local + (var scaled (closure ((int)) int) + (closure ((x int)) int (scale) (return (* x scale)))) + (printf "captured global: %d\n" (scaled 4)) + + (var doubled (closure ((int)) int) + (closure ((x int)) int (double-it) (return (double-it x)))) + (printf "captured function: %d\n" (doubled 4)) + (return 0))