4e987026 |
;;; strictness.scm -- strictness analyzer
;;;
;;; author : Sandra Loosemore
;;; date : 28 May 1992
;;;
;;; The algorithm used here follows Consel, "Fast Strictness Analysis
;;; Via Symbolic Fixpoint Interation".
;;;
;;; The basic idea is to do a traversal of the flic structure, building
;;; a boolean term that represents the strictness of each subexpression.
;;; The boolean terms are composed of ands & ors of the argument variables
;;; to each function. After traversing the body of the function, we can
;;; determine which argument variables are strict by examining the
;;; corresponding term, and then we can update the strictness attribute
;;; of the var that names the function.
;;;
;;; Another traversal needs to be done to attach strictness properties
;;; to locally bound variables.
;;; Here's the main entry point.
(define (strictness-analysis-top big-let)
(fun-strictness-walk big-let)
(var-strictness-walk big-let '() '())
;; *** This probably belongs somewhere else?
(do-box-analysis big-let '() '() '#t)
big-let)
;;;======================================================================
;;; Function strictness analyzer code walk
;;;======================================================================
;;; This actually involves two code walkers. The first merely traverses
;;; structure and identifies function definitions. The second traverses
;;; the definitions of the functions to compute their strictness.
;;; Fun-strictness-walk is the walker to find function definitions.
;;; This is trivial for everything other than flic-let.
(define-flic-walker fun-strictness-walk (object))
(define-fun-strictness-walk flic-lambda (object)
(fun-strictness-walk (flic-lambda-body object)))
(define-fun-strictness-walk flic-let (object)
(if (flic-let-recursive? object)
(fun-strictness-walk-letrec object)
(fun-strictness-walk-let* object))
(dolist (v (flic-let-bindings object))
(fun-strictness-walk (var-value v)))
(fun-strictness-walk (flic-let-body object)))
(define-fun-strictness-walk flic-app (object)
(fun-strictness-walk (flic-app-fn object))
(for-each (function fun-strictness-walk) (flic-app-args object)))
(define-fun-strictness-walk flic-ref (object)
(declare (ignore object))
'#f)
(define-fun-strictness-walk flic-pack (object)
(declare (ignore object))
'#f)
(define-fun-strictness-walk flic-const (object)
(declare (ignore object))
'#f)
(define-fun-strictness-walk flic-case-block (object)
(for-each (function fun-strictness-walk) (flic-case-block-exps object)))
(define-fun-strictness-walk flic-return-from (object)
(fun-strictness-walk (flic-return-from-exp object)))
(define-fun-strictness-walk flic-and (object)
(for-each (function fun-strictness-walk) (flic-and-exps object)))
(define-fun-strictness-walk flic-if (object)
(fun-strictness-walk (flic-if-test-exp object))
(fun-strictness-walk (flic-if-then-exp object))
(fun-strictness-walk (flic-if-else-exp object)))
(define-fun-strictness-walk flic-sel (object)
(fun-strictness-walk (flic-sel-exp object)))
(define-fun-strictness-walk flic-is-constructor (object)
(fun-strictness-walk (flic-is-constructor-exp object)))
(define-fun-strictness-walk flic-con-number (object)
(fun-strictness-walk (flic-con-number-exp object)))
(define-fun-strictness-walk flic-void (object)
(declare (ignore object))
'#f)
;;; Here is the magic for let bindings of function definitions.
;;; Sequential bindings are easy. For recursive bindings, we must
;;; keep track of mutually recursive functions.
;;; If a function binding has a strictness annotation attached,
;;; do not mess with it further.
(define (fun-strictness-walk-let* object)
(dolist (var (flic-let-bindings object))
(let ((val (var-value var)))
(when (is-type? 'flic-lambda val)
(if (var-strictness var)
(mark-argument-strictness
(var-strictness var) (flic-lambda-vars val))
(compute-function-strictness var val '())))
)))
(define (fun-strictness-walk-letrec object)
(let ((stack '()))
(dolist (var (flic-let-bindings object))
(let ((val (var-value var)))
(if (and (is-type? 'flic-lambda val) (not (var-strictness var)))
(setf stack (add-recursive-function-1 var (init-var-env) stack)))))
(dolist (var (flic-let-bindings object))
(let ((val (var-value var)))
(when (is-type? 'flic-lambda val)
(if (var-strictness var)
(mark-argument-strictness
(var-strictness var) (flic-lambda-vars val))
(compute-function-strictness var val stack)))
))))
(define (compute-function-strictness var val stack)
(let* ((vars (flic-lambda-vars val))
(env (add-var-binding-n vars (map (function list) vars)
(init-var-env)))
(term (compute-strictness-walk (flic-lambda-body val) env stack)))
(when (eq? term '#t)
(signal-infinite-loop-function var)
(setf (flic-lambda-body val)
(make-infinite-loop-error
(format '#f "Function ~s has an infinite loop." var))))
(setf (var-strictness var) (munge-strictness-terms term vars))))
(define (signal-infinite-loop-function var)
(recoverable-error 'infinite-loop-function
"Function ~s has an infinite loop."
var))
(define (make-infinite-loop-error msg)
(make-flic-app
(make-flic-ref (core-symbol "error"))
(list (make-flic-const msg))
'#t))
;;; compute-strictness-walk is the traversal to compute strictness
;;; terms.
;;; The purpose of the env is to map locally bound variables onto
;;; strictness terms which are expressed as lists of argument variables
;;; to the function being analyzed.
;;; The purpose of the stack is to keep track of recursive function
;;; walks and recognize when we've reached a fixed point.
(define-flic-walker compute-strictness-walk (object env stack))
;;; Making a function never forces anything.
(define-compute-strictness-walk flic-lambda (object env stack)
(declare (ignore object env stack))
'#f)
;;; For let, add bindings to environment and get strictness of body.
(define-compute-strictness-walk flic-let (object env stack)
(let ((bindings (flic-let-bindings object))
(body (flic-let-body object))
(recursive? (flic-let-recursive? object)))
(if recursive?
;; Must add stuff to env and stack before traversing anything.
(begin
(dolist (var bindings)
(setf env (add-var-binding-1 var '#f env)))
(dolist (var bindings)
(let ((val (var-value var)))
(when (is-type? 'flic-lambda val)
(setf stack (add-recursive-function-1 var env stack)))))
(dolist (var bindings)
(let ((val (var-value var)))
(set-var-env var env (compute-strictness-walk val env stack)))))
;; Otherwise just do things sequentially.
;; Note that even though there is no possibility of recursion
;; here, we must add stuff to the stack anyway so that we can
;; walk calls in the correct env.
(dolist (var bindings)
(let ((val (var-value var)))
(when (is-type? 'flic-lambda val)
(setf stack (add-recursive-function-1 var env stack)))
(setf env
(add-var-binding-1
var (compute-strictness-walk val env stack) env)))))
(compute-strictness-walk body env stack)))
;;; Treat explicit, saturated calls to named functions specially.
(define-compute-strictness-walk flic-app (object env stack)
(let ((fn (flic-app-fn object))
(args (flic-app-args object))
(saturated? (flic-app-saturated? object)))
(cond ((and (is-type? 'flic-ref fn) saturated?)
;; Special handling for named functions.
(compute-application-strictness
(flic-ref-var fn)
args env stack))
((and (is-type? 'flic-pack fn) saturated?)
;; Similarly for constructor applications, but we always
;; know which arguments are strict in advance.
(compute-application-strictness-aux
(con-slot-strict? (flic-pack-con fn))
args env stack))
(else
;; Otherwise, we know that the function expression is going to
;; be forced, but all of its arguments are lazy. So ignore the
;; arguments in computing the strictness of the whole expression.
(compute-strictness-walk fn env stack)))))
(define (compute-application-strictness var args env stack)
(let* ((strictness (var-strictness var))
(info '#f)
(arg-strictness-list '#f))
(cond ((eq? var (core-symbol "error"))
;; This expression will return bottom no matter what.
'error)
(strictness
;; We've already completed the walk for this function and
;; determined which of its arguments are strict.
;; The strictness expression for the application is the
;; OR of the strictness of its non-lazy arguments.
(compute-application-strictness-aux strictness args env stack))
((get-recursive-function-trace
(setf arg-strictness-list
(map (lambda (a) (compute-strictness-walk a env stack))
args))
(setf info (get-recursive-function var stack)))
;; We're already tracing this call. Return true to
;; terminate the fixpoint iteration.
'#t)
(else
;; Otherwise, begin a new trace instance.
;; Add stuff to the saved var-env to map references to
;; the argument variables to the strictness terms for
;; the actual arguments at this call site.
;; References to closed-over variables within the function
;; use the strictness values that were stored in the env
;; at the point of function definition.
(let* ((env (get-recursive-function-env info))
(lambda (var-value var))
(body (flic-lambda-body lambda))
(vars (flic-lambda-vars lambda))
(result '#f))
(push-recursive-function-trace arg-strictness-list info)
(setf result
(compute-strictness-walk
body
(add-var-binding-n vars arg-strictness-list env)
stack))
(pop-recursive-function-trace info)
result))
)))
(define (compute-application-strictness-aux strictness args env stack)
(make-or-term
(map (lambda (strict? arg)
(if strict? (compute-strictness-walk arg env stack) '#f))
strictness args)))
;;; For a reference, look up the term associated with the variable in env.
;;; If not present in the environment, ignore it; the binding was established
;;; outside the scope of the function being analyzed.
(define-compute-strictness-walk flic-ref (object env stack)
(declare (ignore stack))
(get-var-env (flic-ref-var object) env))
;;; References to constants or constructors never fail.
(define-compute-strictness-walk flic-const (object env stack)
(declare (ignore object env stack))
'#f)
(define-compute-strictness-walk flic-pack (object env stack)
(declare (ignore object env stack))
'#f)
;;; The first clause of a case-block is the only one that is always
;;; executed, so it is the only one that affects the strictness of
;;; the overall expression.
(define-compute-strictness-walk flic-case-block (object env stack)
(compute-strictness-walk (car (flic-case-block-exps object)) env stack))
;;; Return-from fails if its subexpression fails.
(define-compute-strictness-walk flic-return-from (object env stack)
(compute-strictness-walk (flic-return-from-exp object) env stack))
;;; For and, the first subexpression is the only one that is always
;;; executed, so it is the only one that affects the strictness of
;;; the overall expression.
(define-compute-strictness-walk flic-and (object env stack)
(compute-strictness-walk (car (flic-and-exps object)) env stack))
;;; The strictness of an IF is the strictness of the test OR'ed
;;; with the AND of the strictness of its branches.
(define-compute-strictness-walk flic-if (object env stack)
(make-or-term-2
(compute-strictness-walk (flic-if-test-exp object) env stack)
(make-and-term-2
(compute-strictness-walk (flic-if-then-exp object) env stack)
(compute-strictness-walk (flic-if-else-exp object) env stack))))
;;; Selecting a component of a data structure causes it to be forced,
;;; so propagate the strictness of the subexpression upwards.
(define-compute-strictness-walk flic-sel (object env stack)
(compute-strictness-walk (flic-sel-exp object) env stack))
;;; Is-constructor and con-number force their subexpressions.
(define-compute-strictness-walk flic-is-constructor (object env stack)
(compute-strictness-walk (flic-is-constructor-exp object) env stack))
(define-compute-strictness-walk flic-con-number (object env stack)
(compute-strictness-walk (flic-con-number-exp object) env stack))
(define-compute-strictness-walk flic-void (object env stack)
(declare (ignore object env stack))
'#f)
;;;======================================================================
;;; Utilities for managing the env
;;;======================================================================
;;; The env is just an a-list.
(define (init-var-env)
'())
(define (add-var-binding-1 var binding env)
(cons (cons var binding) env))
(define (add-var-binding-n vars bindings env)
(if (null? vars)
env
(add-var-binding-n (cdr vars) (cdr bindings)
(cons (cons (car vars) (car bindings)) env))))
(define (get-var-env var env)
(let ((stuff (assq var env)))
(if stuff
(cdr stuff)
'#f)))
(define (set-var-env var env new-value)
(let ((stuff (assq var env)))
(if stuff
(setf (cdr stuff) new-value)
(error "Can't find binding for ~s in environment." var))))
;;;======================================================================
;;; Utilities for managing the stack
;;;======================================================================
;;; For now, the stack is just an a-list too.
;;; Some sort of hashing scheme could also be used instead of a linear
;;; search, but if the iteration depth for the fixpoint analysis is
;;; small, it's probably not worth the trouble.
(define (add-recursive-function-1 var env stack)
(cons (list var env '()) stack))
(define (get-recursive-function var stack)
(or (assq var stack)
(error "Can't find entry for ~s in stack." var)))
(define (get-recursive-function-env entry)
(cadr entry))
(define (push-recursive-function-trace new-args entry)
(push new-args (caddr entry)))
(define (pop-recursive-function-trace entry)
(pop (caddr entry)))
(define (get-recursive-function-trace args entry)
(get-recursive-function-trace-aux args (caddr entry)))
(define (get-recursive-function-trace-aux args list)
(cond ((null? list)
'#f)
((every (function term=) args (car list))
'#t)
(else
(get-recursive-function-trace-aux args (cdr list)))))
;;;======================================================================
;;; Utilities for boolean terms
;;;======================================================================
;;; A term is either #t, #f, the symbol 'error, or a list of variables
;;; (which are implicitly or'ed together).
;;; #t and 'error are treated identically, except that #t indicates
;;; failure because of infinite recursion and 'error indicates failure
;;; due to a call to the error function.
;;; In general, AND terms add nothing to the result, so to reduce
;;; needless computation we generally reduce (and a b) to #f.
;;; Make an OR term. First look for some obvious special cases as an
;;; efficiency hack, otherwise fall through to more general code.
(define (make-or-term terms)
(cond ((null? terms)
'#f)
((null? (cdr terms))
(car terms))
((eq? (car terms) '#t)
'#t)
((eq? (car terms) 'error)
'error)
((eq? (car terms) '#f)
(make-or-term (cdr terms)))
(else
(make-or-term-2 (car terms) (make-or-term (cdr terms))))))
(define (make-or-term-2 term1 term2)
(cond ((eq? term2 '#t)
'#t)
((eq? term2 'error)
'error)
((eq? term2 '#f)
term1)
((eq? term1 '#t)
'#t)
((eq? term1 'error)
'error)
((eq? term1 '#f)
term2)
;; At this point we know both terms are variable lists.
((implies? term2 term1)
term2)
((implies? term1 term2)
term1)
(else
(merge-list-terms term1 term2))))
;;; Merge the two lists, throwing out duplicate variables.
(define (merge-list-terms list1 list2)
(cond ((null? list1)
list2)
((null? list2)
list1)
((eq? (car list1) (car list2))
(cons (car list1) (merge-list-terms (cdr list1) (cdr list2))))
((var< (car list1) (car list2))
(cons (car list1) (merge-list-terms (cdr list1) list2)))
(else
(cons (car list2) (merge-list-terms list1 (cdr list2))))))
;;; Helper function: does term1 imply term2?
;;; True if every subterm of term2 is also included in term1.
(define (implies? term1 term2)
(every (lambda (v2) (memq v2 term1)) term2))
;;; Make an AND term. Because we don't want to build up arbitrarily
;;; complex AND expressions, basically just compute an OR list that
;;; represents the intersection of the subterms.
(define (make-and-term terms)
(cond ((null? terms)
'#f)
((null? (cdr terms))
(car terms))
((eq? (car terms) '#t)
(make-and-term (cdr terms)))
((eq? (car terms) 'error)
(make-and-term (cdr terms)))
((eq? (car terms) '#f)
'#f)
(else
(make-and-term-2 (car terms) (make-and-term (cdr terms))))))
(define (make-and-term-2 term1 term2)
(cond ((eq? term2 '#t)
term1)
((eq? term2 'error)
term1)
((eq? term2 '#f)
'#f)
((eq? term1 '#t)
term2)
((eq? term1 'error)
term2)
((eq? term1 '#f)
'#f)
;; At this point we know both terms are variable lists.
((implies? term2 term1)
term1)
((implies? term1 term2)
term2)
(else
(let ((result '()))
(dolist (v term1)
(if (memq v term2)
(push v result)))
(if (null? result)
'#f
(nreverse result))))
))
;;; Subterms of an and/or term are always sorted, so that to compare
;;; two terms we can just compare subterms componentwise.
(define (term= term1 term2)
(or (eq? term1 term2)
(and (pair? term1)
(pair? term2)
(eq? (car term1) (car term2))
(term= (cdr term1) (cdr term2)))))
;;; Variables within an OR-list are sorted alphabetically by names.
(define (var< var1 var2)
(string<? (symbol->string (def-name var1))
(symbol->string (def-name var2))))
;;; Determine which of the vars are present in the term.
(define (munge-strictness-terms term vars)
(map (lambda (v)
(setf (var-strict? v)
(cond ((var-force-strict? v)
'#t)
((eq? term '#t)
'#t)
((eq? term 'error)
'#t)
((eq? term '#f)
'#f)
((memq v term)
'#t)
(else
'#f))))
vars))
(define (mark-argument-strictness strictness vars)
(map (lambda (s v) (setf (var-strict? v) s)) strictness vars))
;;;======================================================================
;;; Variable strictness propagation code walk
;;;======================================================================
;;; Walk the code, marking any vars found in strict contexts as strict.
;;; Locally bound variables are consed onto the varlist. This is
;;; used to determine which variables can be marked as strict when they
;;; appear in strict contexts.
;;; When walking something that does not appear in a strict context
;;; or that is not always evaluated, reinitialize varlist to the empty
;;; list.
;;; The stack is used to keep track of variables that have not been
;;; initialized yet, so that we can detect some kinds of infinite loops.
;;; When walking something that is not always evaluated, reset this to
;;; the empty list.
(define-flic-walker var-strictness-walk (object varlist stack))
;;; Since the body of the lambda might not be evaluated, reset
;;; both varlist and stack.
(define-var-strictness-walk flic-lambda (object varlist stack)
(declare (ignore varlist stack))
(var-strictness-walk (flic-lambda-body object) '() '()))
;;; The basic idea for let is to find the variables that are strict in
;;; the body first, and propagate that information backwards to the
;;; binding initializers.
(define-var-strictness-walk flic-let (object varlist stack)
(let ((bindings (flic-let-bindings object)))
(var-strictness-walk-let-aux
bindings
(flic-let-body object)
(append bindings varlist)
(append bindings stack)
(flic-let-recursive? object))))
(define (var-strictness-walk-let-aux bindings body varlist stack recursive?)
(if (null? bindings)
(var-strictness-walk body varlist stack)
(begin
(var-strictness-walk-let-aux
(cdr bindings) body varlist (cdr stack) recursive?)
(let* ((var (car bindings))
(val (var-value var)))
(cond ((var-strict? var)
;; Recursive variables have to be set back to unstrict
;; because the value form might contain forward references.
;; The box analyzer will set them to strict again if the
;; value forms are safe.
(when recursive? (setf (var-strict? var) '#f))
;; Detect x = 1 + x circularities here
(var-strictness-walk val varlist stack))
((flic-exp-strict-result? val)
;; The val is going to be wrapped in a delay.
(var-strictness-walk val '() '()))
(else
;; Watch out for x = x and x = cdr x circularities.
;; *** I am still a little confused about this. It
;; *** seems like the stack should be passed through
;; *** when walking already-boxed values that appear as
;; *** non-strict function arguments as well, but doing
;; *** so generates some apparently bogus complaints
;; *** about infinite loops. So maybe doing it here
;; *** is incorrect too, and we just haven't run across
;; *** a test case that triggers it???
(var-strictness-walk val '() stack))
)))))
(define (flic-exp-strict-result? val)
(cond ((is-type? 'flic-ref val)
(var-strict? (flic-ref-var val)))
((is-type? 'flic-sel val)
(list-ref (con-slot-strict? (flic-sel-con val)) (flic-sel-i val)))
(else
'#t)))
(define-var-strictness-walk flic-app (object varlist stack)
(let ((fn (flic-app-fn object))
(args (flic-app-args object))
(saturated? (flic-app-saturated? object)))
(cond ((and saturated? (is-type? 'flic-ref fn))
;; Strictness of function should be stored on var
(do-var-strictness-flic-app-aux
(var-strictness (flic-ref-var fn))
fn args varlist stack))
((and saturated? (is-type? 'flic-pack fn))
;; Strictness of constructor should be stored on con
(do-var-strictness-flic-app-aux
(con-slot-strict? (flic-pack-con fn))
fn args varlist stack))
(else
;; All arguments are non-strict
(var-strictness-walk fn varlist stack)
(dolist (a args)
(var-strictness-walk a '() '()))))))
(define (do-var-strictness-flic-app-aux strictness fn args varlist stack)
(when (not strictness)
(error "Can't find strictness for function ~s." fn))
(dolist (a args)
(if (pop strictness)
(var-strictness-walk a varlist stack)
(var-strictness-walk a '() '()))))
(define-var-strictness-walk flic-ref (object varlist stack)
(let ((var (flic-ref-var object)))
(cond ((memq var stack)
;; Circular variable definition detected.
(signal-infinite-loop-variable var)
(setf (var-value var)
(make-infinite-loop-error
(format '#f "Variable ~s has an infinite loop." var))))
((memq var varlist)
(setf (var-strict? var) '#t))
(else
'#f))))
(define (signal-infinite-loop-variable var)
(recoverable-error 'infinite-loop-variable
"Variable ~s has an infinite loop."
var))
(define-var-strictness-walk flic-const (object varlist stack)
(declare (ignore object varlist stack))
'#f)
(define-var-strictness-walk flic-pack (object varlist stack)
(declare (ignore object varlist stack))
'#f)
(define-var-strictness-walk flic-case-block (object varlist stack)
(var-strictness-walk (car (flic-case-block-exps object)) varlist stack)
(dolist (exp (cdr (flic-case-block-exps object)))
(var-strictness-walk exp '() '())))
(define-var-strictness-walk flic-return-from (object varlist stack)
(var-strictness-walk (flic-return-from-exp object) varlist stack))
(define-var-strictness-walk flic-and (object varlist stack)
(var-strictness-walk (car (flic-and-exps object)) varlist stack)
(dolist (exp (cdr (flic-and-exps object)))
(var-strictness-walk exp '() '())))
(define-var-strictness-walk flic-if (object varlist stack)
(var-strictness-walk (flic-if-test-exp object) varlist stack)
(var-strictness-walk (flic-if-then-exp object) '() '())
(var-strictness-walk (flic-if-else-exp object) '() '()))
(define-var-strictness-walk flic-sel (object varlist stack)
(var-strictness-walk (flic-sel-exp object) varlist stack))
(define-var-strictness-walk flic-is-constructor (object varlist stack)
(var-strictness-walk (flic-is-constructor-exp object) varlist stack))
(define-var-strictness-walk flic-con-number (object varlist stack)
(var-strictness-walk (flic-con-number-exp object) varlist stack))
(define-var-strictness-walk flic-void (object varlist stack)
(declare (ignore object varlist stack))
'#f)
;;;======================================================================
;;; Printer support
;;;======================================================================
(define (strictness-analysis-printer big-let)
(print-strictness big-let 0))
(define (print-strictness-list list depth)
(dolist (o list)
(print-strictness o depth)))
(define (print-strictness-indent depth)
(dotimes (i (* 2 depth))
(declare (ignorable i))
(write-char #\space)))
(define (strictness-string bool)
(if bool "#t" "#f"))
(define-flic-walker print-strictness (object depth))
(define-print-strictness flic-lambda (object depth)
(print-strictness-indent depth)
(format '#t "In anonymous function:~%")
(print-strictness (flic-lambda-body object) (1+ depth)))
(define-print-strictness flic-let (object depth)
(dolist (var (flic-let-bindings object))
(let ((val (var-value var)))
(if (is-type? 'flic-lambda val)
(begin
(print-strictness-indent depth)
(format '#t "Function ~s has argument strictness ~a.~%"
var
(map (function strictness-string) (var-strictness var)))
(print-strictness (flic-lambda-body val) (1+ depth)))
(begin
(print-strictness-indent depth)
(format '#t "Variable ~s has strictness ~a.~%"
var
(strictness-string (var-strict? var)))
(print-strictness val depth)))))
(print-strictness (flic-let-body object) depth))
(define-print-strictness flic-app (object depth)
(print-strictness (flic-app-fn object) depth)
(print-strictness-list (flic-app-args object) depth))
(define-print-strictness flic-ref (object depth)
(declare (ignore object depth))
'#f)
(define-print-strictness flic-const (object depth)
(declare (ignore object depth))
'#f)
(define-print-strictness flic-pack (object depth)
(declare (ignore object depth))
'#f)
(define-print-strictness flic-case-block (object depth)
(print-strictness-list (flic-case-block-exps object) depth))
(define-print-strictness flic-return-from (object depth)
(print-strictness (flic-return-from-exp object) depth))
(define-print-strictness flic-and (object depth)
(print-strictness-list (flic-and-exps object) depth))
(define-print-strictness flic-if (object depth)
(print-strictness (flic-if-test-exp object) depth)
(print-strictness (flic-if-then-exp object) depth)
(print-strictness (flic-if-else-exp object) depth))
(define-print-strictness flic-sel (object depth)
(print-strictness (flic-sel-exp object) depth))
(define-print-strictness flic-is-constructor (object depth)
(print-strictness (flic-is-constructor-exp object) depth))
(define-print-strictness flic-con-number (object depth)
(print-strictness (flic-con-number-exp object) depth))
(define-print-strictness flic-void (object depth)
(declare (ignore object depth))
'#f)
|