keep closure signatures apart when mangled
The argument list was flattened with one separator throughout, so `((long long))' and `((long) (long))' named one struct. A capture borrowing a name looked only at the local scope chain.
This commit is contained in:
3
Makefile
3
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
|
||||
|
||||
22
semen.scm
22
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))))
|
||||
|
||||
52
tests/sex-programs/closure-signatures.sex
Normal file
52
tests/sex-programs/closure-signatures.sex
Normal file
@@ -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))
|
||||
Reference in New Issue
Block a user