declare the closure environment in c99
`max_align_t' is C11 and the union goes into every unit, so a program with no closure in it stopped building under -std=c99. `unify' also bound a rigid variable one way round only.
This commit is contained in:
2
Makefile
2
Makefile
@@ -99,7 +99,7 @@ sextest:
|
|||||||
SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \
|
SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \
|
||||||
feature-flags lambdas compound-literals closures fixpoint \
|
feature-flags lambdas compound-literals closures fixpoint \
|
||||||
wildcards inference type-shapes unnamed-params \
|
wildcards inference type-shapes unnamed-params \
|
||||||
closure-signatures
|
closure-signatures c99
|
||||||
|
|
||||||
# Multi-module linking is checked end to end; see tests/modules/Makefile.
|
# Multi-module linking is checked end to end; see tests/modules/Makefile.
|
||||||
check-modules: sexc
|
check-modules: sexc
|
||||||
|
|||||||
10
infer.scm
10
infer.scm
@@ -509,10 +509,12 @@
|
|||||||
((eq? a b) #t)
|
((eq? a b) #t)
|
||||||
((unknown-type? a) #t)
|
((unknown-type? a) #t)
|
||||||
((unknown-type? b) #t)
|
((unknown-type? b) #t)
|
||||||
((and (tvar? a) (tvar? b) (tvar-rigid? b) (not (tvar-rigid? a)))
|
;; Whichever side is free takes the binding, so that a rigid
|
||||||
(bind-tvar! a b form))
|
;; variable is solved *to* rather than solved, in either order.
|
||||||
((tvar? a) (bind-tvar! a b form))
|
;; Both rigid and distinct is the mismatch `eq?' above let through.
|
||||||
((tvar? b) (bind-tvar! b a form))
|
((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
|
;; A typedef unifies as what it stands for. Its name survives in
|
||||||
;; whichever side is printed later, since neither side is rebuilt.
|
;; whichever side is printed later, since neither side is rebuilt.
|
||||||
((alias-type? a) (unify (alias-expansion a) b form))
|
((alias-type? a) (unify (alias-expansion a) b form))
|
||||||
|
|||||||
14
semen.scm
14
semen.scm
@@ -693,13 +693,19 @@
|
|||||||
|
|
||||||
(define +closure-env-bytes+ 16)
|
(define +closure-env-bytes+ 16)
|
||||||
|
|
||||||
;;; +closure-env-bytes+ for maximum capacity, max-align-t for
|
;;; +closure-env-bytes+ for maximum capacity, and an alignment wide
|
||||||
;;; effectiveness, hence union
|
;;; enough for anything that fits in them, hence union. `max_align_t'
|
||||||
|
;;; would say that in one word, but it is C11 and the target is C99, so
|
||||||
|
;;; the widest built-ins say it instead: a union is aligned for the
|
||||||
|
;;; strictest of its members.
|
||||||
(define +closure-env-type+ 'ƛenv)
|
(define +closure-env-type+ 'ƛenv)
|
||||||
|
|
||||||
(define (closure-env-declaration)
|
(define (closure-env-declaration)
|
||||||
`(union ,+closure-env-type+ ((align max-align-t)
|
`(union ,+closure-env-type+ ((bytes (¤ char ,+closure-env-bytes+))
|
||||||
(bytes (¤ char ,+closure-env-bytes+)))))
|
(align-integer (long long))
|
||||||
|
(align-real (long double))
|
||||||
|
(align-pointer (* void))
|
||||||
|
(align-code (fn ((* void)) void)))))
|
||||||
|
|
||||||
(define +closure-structs+ (make-hash-table))
|
(define +closure-structs+ (make-hash-table))
|
||||||
(define +closure-forwards+ (make-hash-table))
|
(define +closure-forwards+ (make-hash-table))
|
||||||
|
|||||||
@@ -183,7 +183,17 @@
|
|||||||
(test-assert "an ordinary variable binds to it instead"
|
(test-assert "an ordinary variable binds to it instead"
|
||||||
(unify a r #f))
|
(unify a r #f))
|
||||||
;; An unsolved variable resolves to itself.
|
;; 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-group "constraints"
|
||||||
(test #t (entails? 'numeric (parse-type 'int)))
|
(test #t (entails? 'numeric (parse-type 'int)))
|
||||||
|
|||||||
28
tests/sex-programs/c99.sex
Normal file
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))
|
||||||
Reference in New Issue
Block a user