diff --git a/Makefile b/Makefile index 664ff72..a769559 100644 --- a/Makefile +++ b/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 diff --git a/infer.scm b/infer.scm index 64b19d5..3ba37b0 100644 --- a/infer.scm +++ b/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)) diff --git a/semen.scm b/semen.scm index c547e25..c47866f 100644 --- a/semen.scm +++ b/semen.scm @@ -693,13 +693,19 @@ (define +closure-env-bytes+ 16) -;;; +closure-env-bytes+ for maximum capacity, max-align-t for -;;; effectiveness, hence union +;;; +closure-env-bytes+ for maximum capacity, and an alignment wide +;;; 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-declaration) - `(union ,+closure-env-type+ ((align max-align-t) - (bytes (¤ char ,+closure-env-bytes+))))) + `(union ,+closure-env-type+ ((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-forwards+ (make-hash-table)) diff --git a/tests/infer.scm b/tests/infer.scm index 587fe1e..2d13c64 100644 --- a/tests/infer.scm +++ b/tests/infer.scm @@ -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))) diff --git a/tests/sex-programs/c99.sex b/tests/sex-programs/c99.sex new file mode 100644 index 0000000..f65cd37 --- /dev/null +++ b/tests/sex-programs/c99.sex @@ -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))