~ chicken-core (chicken-5) 006bd0e45ef861f52e9846736fa943f439d8a2cd


commit 006bd0e45ef861f52e9846736fa943f439d8a2cd
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Tue Aug 23 22:56:45 2011 +0200
Commit:     felix <felix@call-with-current-continuation.org>
CommitDate: Tue Aug 23 22:56:45 2011 +0200

    purity

diff --git a/chicken-syntax.scm b/chicken-syntax.scm
index 284b6ddb..ec27c9dd 100644
--- a/chicken-syntax.scm
+++ b/chicken-syntax.scm
@@ -1110,13 +1110,14 @@
 	'(##core#undefined)
 	(let* ((type1 (##sys#strip-syntax (caddr x)))
 	       (name1 (cadr x)))
-	  (let-values (((type pred)
+	  (let-values (((type pred pure)
 			(##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 pure `((pure ,name1)) '())
 		     (enforce-argument-types ,name1)
 		     ,@(if pred `((predicate (,name1 ,pred))) '()))))))))))
 
@@ -1225,7 +1226,7 @@
 				 (if (and rtypes (pair? rtypes))
 				     (list
 				      (map (lambda (rt)
-					     (let-values (((t _) 
+					     (let-values (((t pred pure) 
 							   (##compiler#validate-type rt #f)))
 					       (or t
 						   (syntax-error
@@ -1250,7 +1251,8 @@
 			(cond ((symbol? arg)
 			       (loop (cdr args) (cons arg anames) (cons '* atypes)))
 			      ((and (list? arg) (fx= 2 (length arg)) (symbol? (car arg)))
-			       (let-values (((t _) (##compiler#validate-type (cadr arg) #f)))
+			       (let-values (((t pred pure)
+					     (##compiler#validate-type (cadr arg) #f)))
 				 (if t
 				     (loop
 				      (cdr args)
diff --git a/compiler.scm b/compiler.scm
index 15f17f81..a73202e8 100644
--- a/compiler.scm
+++ b/compiler.scm
@@ -1503,7 +1503,7 @@
 	       (warning "illegal type declaration" (##sys#strip-syntax spec))
 	       (let ((name (##sys#globalize (car spec) se))
 		     (type (##sys#strip-syntax (cadr spec))))
-		 (let-values (((type pred) (validate-type type name)))
+		 (let-values (((type pred pure) (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
@@ -1515,6 +1515,8 @@
 			    (set-car! (cdr type) name))
 			  (mark-variable name '##compiler#type type)
 			  (mark-variable name '##compiler#declared-type)
+			  (when pure
+			    (mark-variable name '##compiler#pure #t))
 			  (when pred
 			    (mark-variable name '##compiler#predicate pred))
 			  (when (pair? (cddr spec))
@@ -1532,7 +1534,7 @@
 	   (cond ((and (list? spec) (symbol? (car spec)) (= 2 (length spec)))
 		  (let ((name (##sys#globalize (car spec) se))
 			(type (##sys#strip-syntax (cadr spec))))
-		    (let-values (((type pred) (validate-type type name)))
+		    (let-values (((type pred pure) (validate-type type name)))
 		      (if (and type (not pred))
 			  (mark-variable name '##compiler#predicate type)
 			  (warning "illegal `predicate' declaration" spec)))))
diff --git a/defaults.make b/defaults.make
index db4807aa..e37ad02b 100644
--- a/defaults.make
+++ b/defaults.make
@@ -272,7 +272,8 @@ CSI ?= csi$(EXE)
 
 CHICKEN_OPTIONS = -optimize-level 2 -include-path . -include-path $(SRCDIR) -inline -ignore-repository -feature chicken-bootstrap
 ifdef DEBUGBUILD
-CHICKEN_OPTIONS += -feature debugbuild -scrutinize -types $(SRCDIR)types.db -verbose
+#XXX CHICKEN_OPTIONS += -feature debugbuild -scrutinize -types $(SRCDIR)types.db -verbose
+CHICKEN_OPTIONS += -feature debugbuild -verbose
 else
 CHICKEN_OPTIONS += -no-warnings
 endif
diff --git a/manual/Types b/manual/Types
index 1144184e..2e2729fc 100644
--- a/manual/Types
+++ b/manual/Types
@@ -94,6 +94,7 @@ or {{:}} should follow the syntax given below:
 <tr><td>{{(struct STRUCTURENAME)}}</td><td>record structure of given kind</td></tr>
 <tr><td>{{(procedure [NAME] (VALUETYPE ... [#!optional VALUETYPE ...] [#!rest [VALUETYPE]]) . RESULTS)}}</td><td>procedure type, optionally with name</td></tr>
 <tr><td>{{(VALUETYPE ... [#!optional VALUETYPE ...] [#!rest [VALUETYPE]] -> . RESULTS)}}</td><td>alternative procedure type syntax</td></tr>
+<tr><td>{{(VALUETYPE ... [#!optional VALUETYPE ...] [#!rest [VALUETYPE]] --> . RESULTS)}}</td><td>procedure type that is declared as referentially transparent</td></tr>
 <tr><td>{{(VALUETYPE -> VALUETYPE : VALUETYPE)}}</td><td>predicate procedure type</td></tr>
 <tr><td>{{(forall (TYPEVAR ...) VALUETYPE)}}</td><td>polymorphic type</td></tr>
 <tr><td>COMPLEXTYPE</td><td></td></tr>
@@ -179,6 +180,14 @@ procedure will be a predicate, i.e. it accepts a single argument of type
 a true value if the argument is of type {{TYPE}} and false otherwise.
 
 
+==== Purity
+
+Procedure types are assumed to be not referentially transparent. Using
+the {{(... --> ...)}} syntax, you can declare a procedure to be referentially
+transparent, i.e. not causing any side-effects. This gives more opportunities
+for optimization but may not be violated or the results are undefined.
+
+
 ==== Using type information in extensions
 
 Type information of declared toplevel variables can be used in client
diff --git a/scrutinizer.scm b/scrutinizer.scm
index f3fed28b..2bca6c09 100755
--- a/scrutinizer.scm
+++ b/scrutinizer.scm
@@ -729,7 +729,7 @@
 				 r
 				 (map (cut resolve <> typeenv) r)))))))
 		 ((##core#the)
-		  (let-values (((t _) (validate-type (first params) #f)))
+		  (let-values (((t pred pure) (validate-type (first params) #f)))
 		    (let ((rt (walk (first subs) e loc dest tail flow ctags)))
 		      (cond ((eq? rt '*))
 			    ((null? rt)
@@ -1529,6 +1529,9 @@
 ;;; type-db processing
 
 (define (load-type-database name #!optional (path (repository-path)))
+  (define (pure! name)
+    (when enable-specialization 
+      (mark-variable name '##compiler#pure #t)))
   (and-let* ((dbfile (file-exists? (make-pathname path name))))
     (debugging 'p (sprintf "loading type database `~a' ...~%" dbfile))
     (fluid-let ((scrutiny-debug #f))
@@ -1540,17 +1543,14 @@
 		(new
 		 (let adjust ((new (cadr e)))
 		   (if (pair? new)
-		       (cond ((and (list? (car new))
-				   (eq? 'procedure (caar new)))
+		       (cond ((and (vector? (car new))
+				   (eq? 'procedure (vector-ref new 0)))
 			      ;;XXX this format is not used yet:
-			      (let loop ((props (cdar new)))
+			      (let loop ((props (cdr (vector->list (car new)))))
 				(unless (null? props)
 				  (case (car props)
 				    ((pure)
-				     ;;XXX this overwrites a possibly existing 'standard/
-				     ;;    'extended mark - I don't know if this is
-				     ;;    a problem
-				     (mark-variable name '##compiler#pure #t)
+				     (pure! name)
 				     (loop (cdr props)))
 				    ((enforce)
 				     (mark-variable name '##compiler#enforce #t)
@@ -1561,7 +1561,7 @@
 				    (else
 				     (bomb
 				      "load-type-database: invalid procedure-type property"
-				      (car props))))))
+				      (car props) new)))))
 			      `(procedure ,@(cdr new)))
 			     (else 	;XXX old style, remove at some stage
 			      (case (car new)
@@ -1581,7 +1581,7 @@
 		       new))))
 	   ;; validation is needed, even though .types-files can be considered
 	   ;; correct, because type variables have to be renamed:
-	   (let-values (((t _) (validate-type new name)))
+	   (let-values (((t pred pure) (validate-type new name)))
 	     (unless t
 	       (warning "invalid type specification" name new))
 	     (when (and old (not (compatible-types? old t)))
@@ -1607,6 +1607,7 @@
 	   (let ((specs (or (variable-mark sym '##compiler#specializations) '()))
 		 (type (variable-mark sym '##compiler#type))
 		 (pred (variable-mark sym '##compiler#predicate))
+		 (pure (variable-mark sym '##compiler#pure))
 		 (enforce (variable-mark sym '##compiler#enforce)))
 	     (pp (cons*
 		  sym
@@ -1614,11 +1615,10 @@
 		    (if (pair? type)
 			(case (car type)
 			  ((procedure)
-			   `(,(cond ((and enforce pred) 'procedure!?)
-				    (pred 'procedure?)
-				    (enforce 'procedure!)
-				    (else 'procedure))
-			     ,@(if pred (list pred) '())
+			   `(#(procedure
+			       ,@(if enforce '(enforce) '())
+			       ,@(if pred `(predicate ,pred) '())
+			       ,@(if pure '(pure) '()))
 			     ,@(cdr type)))
 			  ((forall)
 			   `(forall ,(second type) ,(wrap (third type))))
@@ -1668,10 +1668,12 @@
   ;; - converts some typenames to struct types (u32vector, etc.)
   ;; - drops "#!key ..." args by converting to #!rest
   ;; - handles "(T1 -> T2 : T3)" (predicate) 
+  ;; - handles "(T1 --> T2 [: T3])" (pure)
   ;; - simplifies result
   ;; - coalesces all "forall" forms into one (remove "forall" if typevar-set is empty)
   ;; - renames type-variables
   (let ((ptype #f)			; (T . PT) | #f
+	(pure #f)
 	(usedvars '())
 	(typevars '()))
     (define (upto lst p)
@@ -1735,22 +1737,26 @@
 		  t))
 	    ((eq? 'deprecated (car t))
 	     (and (= 2 (length t)) (symbol? (second t))))
-	    ((memq '-> t) =>
+	    ((or (memq '--> t) (memq '-> t)) =>
 	     (lambda (p)
-	       (let ((cp (memq ': (cdr p))))
-		 (cond ((not cp) 
-			(validate
-			 `(procedure ,(upto t p) ,@(cdr p))
-			 rec))
-		       ((and (= 5 (length t))
-			     (eq? p (cdr t))
-			     (eq? cp (cdddr t)))
-			(set! t (validate `(procedure (,(first t)) ,(third t)) rec))
-			;; we do it this way to distinguish the "outermost" predicate
-			;; procedure type
-			(set! ptype (cons t (validate (cadr cp))))
-			t)
-		       (else #f)))))
+	       (let* ((puref (eq? '--> (car p)))
+		      (ok (or (not rec) (not puref))))
+		 (set! pure puref)
+		 (let ((cp (memq ': (cdr p))))
+		   (cond ((not cp)
+			  (and ok
+			       (validate
+				`(procedure ,(upto t p) ,@(cdr p))
+				rec)))
+			 ((and (= 5 (length t))
+			       (eq? p (cdr t))
+			       (eq? cp (cdddr t)))
+			  (set! t (validate `(procedure (,(first t)) ,(third t)) rec))
+			  ;; we do it this way to distinguish the "outermost" predicate
+			  ;; procedure type
+			  (set! ptype (cons t (validate (cadr cp))))
+			  (and ok t))
+			 (else #f))))))
 	    ((memq (car t) '(vector list))
 	     (and (= 2 (length t))
 		  (let ((t2 (validate (second t))))
@@ -1792,7 +1798,10 @@
 			    (delete-duplicates typevars eq?))
 			  ,type)))
 	     (let ((type (simplify-type type)))
-	       (values type (and ptype (eq? (car ptype) type) (cdr ptype))))))
+	       (values 
+		type 
+		(and ptype (eq? (car ptype) type) (cdr ptype))
+		pure))))
 	  (else (values #f #f)))))
 
 (define (install-specializations name specs)
@@ -1814,7 +1823,7 @@
 	  (if (and (list? spec) (list? (first spec)))
 	      (let* ((args
 		      (map (lambda (t) 
-			     (let-values (((t2 _) (validate-type t #f)))
+			     (let-values (((t2 pred pure) (validate-type t #f)))
 			       (or t2
 				   (error "invalid argument type in specialization" 
 					  t spec name))))
@@ -1828,7 +1837,7 @@
 		    (cond ((list? (second spec))
 			   (cons
 			    (map (lambda (t)
-				   (let-values (((t2 _) (validate-type t #f)))
+				   (let-values (((t2 pred pure) (validate-type t #f)))
 				     (or t2
 					 (error "invalid result type in specialization" 
 						t spec name))))
Trap