~ chicken-core (chicken-5) 9bb90f7761760efa69bd78c391b9e35bbc5320c9


commit 9bb90f7761760efa69bd78c391b9e35bbc5320c9
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Fri Oct 12 19:07:20 2012 +0200
Commit:     Peter Bex <peter.bex@xs4all.nl>
CommitDate: Fri Oct 12 20:47:52 2012 +0200

    Reduce typeset in alternative conditional branch with predicate.
    
    During flow-analysis, when a predicate is applied to a variable, the
    variable is assumed to have the corresponding type in the consequent
    branch of a conditional that depends on this predicate call. This
    patch adds a small enhancement that, in case the variable type is
    known to be a typeset (an "(or ...)" type), reduces the typeset by
    removing those types that match the predicate-type:
    
    (let ((a ...))  ; say "a" is of type "(or string number)"
      (if (number? a)
          ...           ; "a" is known to be of type "number"
          ...))         ; "a" is now known to be of type "string"  <- new
    
    Here "number" matches the predicate type of "number?" ("number"),
    is removed from the "(or string number)" type, and results in
    type "string" for "a" in the second "if" branch.
    
    Signed-off-by: Peter Bex <peter.bex@xs4all.nl>

diff --git a/scrutinizer.scm b/scrutinizer.scm
index 3ed4753d..b4f4b3dd 100755
--- a/scrutinizer.scm
+++ b/scrutinizer.scm
@@ -447,6 +447,19 @@
 		(make-list argc '*)))
 	  (make-list argc '*)))
 
+    (define (reduce-typeset t pt typeenv)
+      (and-let* ((tnew
+		  (let rec ((t t))
+		    (and (pair? t)
+			 (case (car t)
+			   ((forall) 
+			    (and-let* ((t2 (rec (third t))))
+			      `(forall ,(second t) ,t2)))
+			   ((or) 
+			    `(or ,@(remove (cut match-types <> pt typeenv) (cdr t))))
+			   (else #f))))))
+	(simplify-type tnew)))
+
     (define (walk n e loc dest tail flow ctags) ; returns result specifier
       (let ((subs (node-subexpressions n))
 	    (params (node-parameters n)) 
@@ -717,12 +730,25 @@
 						    (not (get db var 'assigned)) 
 						    (not oparg?))))
 				    (cond (pred
+					   ;;XXX is this needed? "typeenv" is the te of "args",
+					   ;;    not of "pt":
 					   (let ((pt (resolve pt typeenv)))
 					     (d "  predicate `~a' indicates `~a' is ~a in flow ~a"
 						pn var pt (car ctags))
 					     (add-to-blist 
 					      var (car ctags)
-					      (if (and a (type<=? (cdr a) pt)) (cdr a) pt))))
+					      (if (and a (type<=? (cdr a) pt)) (cdr a) pt))
+					     ;; if the variable type is an "or"-type, we can
+					     ;; can remove all elements that match the predicate
+					     ;; type
+					     (when a
+					       ;;XXX hack, again:
+					       (let* ((tenv2 (type-typeenv `(or ,(cdr a) ,pt)))
+						      (at (reduce-typeset (cdr a) pt tenv2)))
+						 (when at
+						   (d "  predicate `~a' indicates `~a' is ~a in flow ~a"
+						      pn var at (cdr ctags))
+						   (add-to-blist var (cdr ctags) at))))))
 					  (a
 					   (when enforces
 					     (let ((ar (cond ((blist-type var flow) =>
diff --git a/tests/scrutiny-tests-3.scm b/tests/scrutiny-tests-3.scm
index 41b46fb7..243a069a 100644
--- a/tests/scrutiny-tests-3.scm
+++ b/tests/scrutiny-tests-3.scm
@@ -10,3 +10,22 @@
  (compiler-typecase vec
    ((vector-of boolean) #f)
    (vector #t)))
+
+
+;;; reduce OR-types in alternative branch of conditional with predicate
+
+(define something)
+
+(let ((x (the (or string number) something)))
+  (if (number? x)
+      (compiler-typecase x
+	(number 1))
+      (compiler-typecase x
+	(string 2))))
+
+(let ((x (the (forall ((a string) (b number)) (or a b)) something)))
+  (if (number? x)
+      (compiler-typecase x
+	(number 3))
+      (compiler-typecase x
+	(string 4))))
Trap