~ chicken-core (chicken-5) fbd01232468c6b939a82bb543169862a9ce4e3af


commit fbd01232468c6b939a82bb543169862a9ce4e3af
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Thu May 26 09:28:27 2011 -0400
Commit:     felix <felix@call-with-current-continuation.org>
CommitDate: Thu May 26 09:28:27 2011 -0400

    predicate syntax for procedure types (as in typed sports equipment)

diff --git a/chicken-syntax.scm b/chicken-syntax.scm
index ef8f9292..9e222e96 100644
--- a/chicken-syntax.scm
+++ b/chicken-syntax.scm
@@ -1120,13 +1120,15 @@
     (if (memq #:csi ##sys#features) 
 	'(##core#undefined)
 	(let* ((type1 (##sys#strip-syntax (caddr x)))
-	       (name1 (cadr x))
-	       (type (##compiler#validate-type type1 (##sys#strip-syntax name1))))
-	  (cond ((not type)
-		 (syntax-error ': "invalid type syntax" name1 type1))
-		(else
-		 `(##core#declare 
-		   (type (,name1 ,type ,@(cdddr x)))))))))))
+	       (name1 (cadr x)))
+	  (let-values (((type pred)
+			(##compiler#validate-type type1 (##sys#strip-syntax name1))))
+	    (cond ((not type)
+		   (syntax-error ': "invalid type syntax" name1 type1))
+		  (else
+		   `(##core#declare 
+		     (type (,name1 ,type ,@(cdddr x)))
+		     ,@(if pred `((predicate (,name1 ,pred))) '()))))))))))
 
 
 ;;; interface definition
diff --git a/compiler.scm b/compiler.scm
index 163d0e6f..ae06a679 100644
--- a/compiler.scm
+++ b/compiler.scm
@@ -1493,8 +1493,8 @@
 	       (warning "illegal type declaration" (##sys#strip-syntax spec))
 	       (let ((name (##sys#globalize (car spec) se))
 		     (type (##sys#strip-syntax (cadr spec))))
-		 (cond ((validate-type type name) =>
-			(lambda (type)
+		 (let-values (((type pred) (validate-type type name)))
+		   (cond (type
 			  ;; HACK: since `:' doesn't have access to the SE, we
 			  ;; fixup the procedure name if type is a named procedure type
 			  ;; (We only have access to the SE for ##sys#globalize in here).
@@ -1505,14 +1505,16 @@
 			    (set-car! (cdr type) name))
 			  (mark-variable name '##compiler#type type)
 			  (mark-variable name '##compiler#declared-type)
+			  (when pred
+			    (mark-variable name '##compiler#predicate pred))
 			  (when (pair? (cddr spec))
 			    (mark-variable
 			     name '##compiler#specializations
 			     (##sys#strip-syntax (cddr spec)))))
-			(else
-			 (warning 
-			  "illegal `type' declaration"
-			  (##sys#strip-syntax spec))))))))
+			 (else
+			  (warning 
+			   "illegal `type' declaration"
+			   (##sys#strip-syntax spec))))))))
 	 (cdr spec)))
        ((predicate)
 	(for-each
@@ -1520,11 +1522,10 @@
 	   (cond ((and (list? spec) (symbol? (car spec)) (= 2 (length spec)))
 		  (let ((name (##sys#globalize (car spec) se))
 			(type (##sys#strip-syntax (cadr spec))))
-		    (cond ((validate-type type name) =>
-			   (lambda (type)
-			     (##sys#put! name '##compiler#predicate type)))
-			  (else
-			   (warning "illegal `predicate' declaration" spec)))))
+		    (let-values (((type pred) (validate-type type name)))
+		      (if (and type (not pred))
+			  (mark-variable name '##compiler#predicate type)
+			  (warning "illegal `predicate' declaration" spec)))))
 		 (else
 		  (warning "illegal `type' declaration item" spec))))
 	 (globalize-all (cdr spec))))
diff --git a/scrutinizer.scm b/scrutinizer.scm
index f81f9270..7044057c 100755
--- a/scrutinizer.scm
+++ b/scrutinizer.scm
@@ -321,14 +321,17 @@
 	    (else (cons (simplify `(or ,(car ts1) ,(car ts2)))
 			(merge-argument-types (cdr ts1) (cdr ts2))))))
 
-    (define (merge-result-types ts1 ts2) ;XXX possibly overly conservative
-      (cond ((null? ts1) ts2)
-	    ((null? ts2) ts1)
-	    ((or (atom? ts1) (atom? ts2)) '*)
-	    ((eq? 'noreturn (car ts1)) ts2)
-	    ((eq? 'noreturn (car ts2)) ts1)
-	    (else (cons (simplify `(or ,(car ts1) ,(car ts2)))
-			(merge-result-types (cdr ts1) (cdr ts2))))))
+    (define (merge-result-types ts11 ts21) ;XXX possibly overly conservative
+      (call/cc
+       (lambda (return)
+	 (let loop ((ts1 ts11) (ts2 ts21))
+	   (cond ((null? ts1) ts2)
+		 ((null? ts2) ts1)
+		 ((or (atom? ts1) (atom? ts2)) (return '*))
+		 ((eq? 'noreturn (car ts1)) (loop (cdr ts1) ts2))
+		 ((eq? 'noreturn (car ts2)) (loop ts1 (cdr ts2)))
+		 (else (cons (simplify `(or ,(car ts1) ,(car ts2)))
+			     (loop (cdr ts1) (cdr ts2)))))))))
 
     (define (match t1 t2)
       (let ((m (match1 t1 t2)))
@@ -973,8 +976,9 @@
 		  (set-car! new 'procedure))
 		(cond-expand
 		  (debugbuild
-		   (unless (validate-type new name)
-		     (warning "invalid type specification" name new)))
+		   (let-values (((t _) (validate-type new name)))
+		     (unless t
+		       (warning "invalid type specification" name new))))
 		  (else))
 		(when (and old (not (equal? old new)))
 		  (##sys#notice
@@ -1054,73 +1058,87 @@
   ;; - returns converted type or #f
   ;; - also converts "(... -> ...)" types
   ;; - drops "#!key ..." args by converting to #!rest
-  (define (upto lst p)
-    (let loop ((lst lst))
-      (cond ((eq? lst p) '())
-	    (else (cons (car lst) (loop (cdr lst)))))))
-  (define (validate-llist llist)
-    (cond ((null? llist) '())
-	  ((symbol? llist) '(#!rest *))
-	  ((not (pair? llist)) #f)
-	  ((eq? '#!optional (car llist))
-	   (let ((l1 (validate-llist (cdr llist))))
-	     (and l1 (cons '#!optional l1))))
-	  ((eq? '#!rest (car llist))
-	   (cond ((null? (cdr llist)) '(#!rest *))
-		 ((not (pair? (cdr llist))) #f)
-		 (else
-		  (let ((l1 (validate (cadr llist))))
-		    (and l1 `(#!rest ,l1))))))
-	  ((eq? '#!key (car llist)) '(#!rest *))
-	  (else
-	   (let* ((l1 (validate (car llist)))
-		  (l2 (validate-llist (cdr llist))))
-	     (and l1 l2 (cons l1 l2))))))
-  (define (validate t)
-    (cond ((memq t '(* string symbol char number boolean list pair
-		       procedure vector null eof undefined port blob
-		       pointer locative fixnum float pointer-vector
-		       deprecated))
-	   t)
-	  ((not (pair? t)) t)
-	  ((eq? 'or (car t)) 
-	   (and (list? t)
-		(let ((ts (map validate (cdr t))))
-		  (and (every identity ts)
-		       `(or ,@ts)))))
-	  ((eq? 'struct (car t))
-	   (and (= 2 (length t))
-		(symbol? (cadr t))
-		t))
-	  ((eq? 'procedure (car t))
-	   (and (pair? (cdr t))
-		(let* ((name (if (symbol? (cadr t))
-				 (cadr t)
-				 name))
-		       (t2 (if (symbol? (cadr t)) (cddr t) (cdr t))))
-		  (and (pair? t2)
-		       (list? (car t2))
-		       (let ((ts (validate-llist (car t2))))
-			 (and ts
-			      (every identity ts)
-			      (let* ((rt2 (cdr t2))
-				     (rt (if (eq? '* rt2) 
-					     rt2
-					     (and (list? rt2)
-						  (let ((rts (map validate rt2)))
-						    (and (every identity rts)
-							 rts))))))
-				(and rt
-				     `(procedure 
-				       ,@(if name (list name) '())
-				       ,ts
-				       ,@rt)))))))))
-	  ((and (pair? (cdr t)) (memq '-> (cdr t))) =>
-	   (lambda (p)
-	     (validate
-	      `(procedure ,(upto t p) ,@(cdr p)))))
-	  (else #f)))
-  (validate type))
+  ;; - handles "(T1 -> T2 : T3)" (predicate) 
+  (let ((ptype #f))			; (T . PT) | #f
+    (define (upto lst p)
+      (let loop ((lst lst))
+	(cond ((eq? lst p) '())
+	      (else (cons (car lst) (loop (cdr lst)))))))
+    (define (validate-llist llist)
+      (cond ((null? llist) '())
+	    ((symbol? llist) '(#!rest *))
+	    ((not (pair? llist)) #f)
+	    ((eq? '#!optional (car llist))
+	     (let ((l1 (validate-llist (cdr llist))))
+	       (and l1 (cons '#!optional l1))))
+	    ((eq? '#!rest (car llist))
+	     (cond ((null? (cdr llist)) '(#!rest *))
+		   ((not (pair? (cdr llist))) #f)
+		   (else
+		    (let ((l1 (validate (cadr llist))))
+		      (and l1 `(#!rest ,l1))))))
+	    ((eq? '#!key (car llist)) '(#!rest *))
+	    (else
+	     (let* ((l1 (validate (car llist)))
+		    (l2 (validate-llist (cdr llist))))
+	       (and l1 l2 (cons l1 l2))))))
+    (define (validate t)
+      (cond ((memq t '(* string symbol char number boolean list pair
+			 procedure vector null eof undefined port blob
+			 pointer locative fixnum float pointer-vector
+			 deprecated))
+	     t)
+	    ((not (pair? t)) t)
+	    ((eq? 'or (car t)) 
+	     (and (list? t)
+		  (let ((ts (map validate (cdr t))))
+		    (and (every identity ts)
+			 `(or ,@ts)))))
+	    ((eq? 'struct (car t))
+	     (and (= 2 (length t))
+		  (symbol? (cadr t))
+		  t))
+	    ((eq? 'procedure (car t))
+	     (and (pair? (cdr t))
+		  (let* ((name (if (symbol? (cadr t))
+				   (cadr t)
+				   name))
+			 (t2 (if (symbol? (cadr t)) (cddr t) (cdr t))))
+		    (and (pair? t2)
+			 (list? (car t2))
+			 (let ((ts (validate-llist (car t2))))
+			   (and ts
+				(every identity ts)
+				(let* ((rt2 (cdr t2))
+				       (rt (if (eq? '* rt2) 
+					       rt2
+					       (and (list? rt2)
+						    (let ((rts (map validate rt2)))
+						      (and (every identity rts)
+							   rts))))))
+				  (and rt
+				       `(procedure 
+					 ,@(if name (list name) '())
+					 ,ts
+					 ,@rt)))))))))
+	    ((and (pair? (cdr t)) (memq '-> (cdr t))) =>
+	     (lambda (p)
+	       (let ((cp (memq ': (cdr t))))
+		 (cond ((not cp) 
+			(validate
+			 `(procedure ,(upto t p) ,@(cdr p))))
+		       ((and (= 5 (length t))
+			     (eq? p (cdr t))
+			     (eq? cp (cdddr t)))
+			(set! t (validate `(procedure (,(first t)) ,(third t))))
+			;; we do it this way to distinguish the "outermost" predicate
+			;; procedure type
+			(set! ptype (cons t (validate (cadr cp))))
+			t)
+		       (else #f)))))
+	    (else #f)))
+    (let ((type (validate type)))
+      (values type (and ptype (eq? (car ptype) type) (cdr ptype))))))
 
 (define (initial-argument-types dest vars argc)
   (if (and dest (variable-mark dest '##compiler#declared-type))
diff --git a/tests/scrutiny-tests.scm b/tests/scrutiny-tests.scm
index c4fd9ea8..786c5d84 100644
--- a/tests/scrutiny-tests.scm
+++ b/tests/scrutiny-tests.scm
@@ -80,3 +80,10 @@
   (let ((y x))
     (string-append x "abc")
     (+ x 3)))				;XXX (+ y 3) does not work yet
+
+;; user-defined predicate
+(: foo7 (* -> bool : string))
+(define (foo7 x) (string x))
+
+(when (foo7 x)
+  (+ x 1))				; will warn about "x" being a string
Trap