* boot-9.scm (process-define-module): Handle #:duplicates.
[bpt/guile.git] / ice-9 / boot-9.scm
index 4bac71a..daa9bf9 100644 (file)
 ;;
 (define (module-symbol-local-binding m v . opt-val)
   (let ((var (module-local-variable m v)))
-    (if var
+    (if (and var (variable-bound? var))
        (variable-ref var)
        (if (not (null? opt-val))
            (car opt-val)
 ;;
 (define (module-symbol-binding m v . opt-val)
   (let ((var (module-variable m v)))
-    (if var
+    (if (and var (variable-bound? var))
        (variable-ref var)
        (if (not (null? opt-val))
            (car opt-val)
                    (cons interface (delq! interface (module-uses module))))
   (module-modified module))
 
+;; MODULE-USE-INTERFACES! module interfaces
+;;
+;; Same as MODULE-USE! but add multiple interfaces and check for duplicates
+;;
+(define (module-use-interfaces! module interfaces)
+  (let* ((duplicates-info (module-duplicates-info module))
+        (duplicates-handlers? (car duplicates-info))
+        (uses (module-uses module)))
+    ;; remove duplicates-interface
+    (set! uses (delq! (cdr duplicates-info) uses))
+    ;; remove interfaces to be added
+    (for-each (lambda (interface)
+               (set! uses (delq! interface uses)))
+             interfaces)
+    ;; add interfaces to use list
+    (set-module-uses! module uses)
+    (for-each (lambda (interface)
+               (and duplicates-handlers?
+                    ;; perform duplicate checking
+                    (process-duplicates module interface))
+               (set! uses (cons interface uses))
+               (set-module-uses! module uses))
+             interfaces)
+    ;; add duplicates interface
+    (if (cdr duplicates-info)
+       (set-module-uses! module (cons (cdr duplicates-info) uses)))
+    (module-modified module)))
+
 \f
 ;;; {Recursive Namespaces}
 ;;;
   (module-ref m '%module-public-interface #f))
 (define (set-module-public-interface! m i)
   (module-define! m '%module-public-interface i))
+(define (module-duplicates-info m)
+  (or (module-ref m '%module-duplicates-info #f) (cons #f #f)))
+(define (set-module-duplicates-info! m i)
+  (module-define! m '%module-duplicates-info i))
 (define (set-system-module! m s)
   (set-procedure-property! (module-eval-closure m) 'system-module s))
 (define the-root-module (make-root-module))
 (define the-scm-module (make-scm-module))
 (set-module-public-interface! the-root-module the-scm-module)
+(set-module-duplicates-info! the-root-module (cons #f #f))
 (set-module-name! the-root-module '(guile))
 (set-module-name! the-scm-module '(guile))
 (set-module-kind! the-scm-module 'interface)
        (let ((interface (make-module 31)))
          (set-module-name! interface (module-name module))
          (set-module-kind! interface 'interface)
-         (set-module-public-interface! module interface))))
+         (set-module-public-interface! module interface)
+         (set-module-duplicates-info! module (cons #f #f)))))
   (if (and (not (memq the-scm-module (module-uses module)))
           (not (eq? module the-root-module)))
-      (set-module-uses! module (append (module-uses module) (list the-scm-module)))))
+      (set-module-uses! module
+                       (append (module-uses module) (list the-scm-module)))))
 
 ;; NOTE: This binding is used in libguile/modules.c.
 ;;
               (re-exports '()))
       (if (null? kws)
          (begin
-           (for-each (lambda (interface)
-                       (module-use! module interface))
-                     (reverse reversed-interfaces))
+           (module-use-interfaces! module (reverse reversed-interfaces))
            (module-export! module exports)
            (module-re-export! module re-exports))
          (case (car kws)
            ((#:pure)
             (purify-module! module)
             (loop (cdr kws) reversed-interfaces exports re-exports))
+           ((#:duplicates)
+            (if (not (pair? (cdr kws)))
+                (unrecognized kws))
+            (set-car! (module-duplicates-info module)
+                      (map (lambda (handler-name)
+                             (or (module-symbol-local-binding
+                                  duplicate-handlers handler-name #f)
+                                 (error "invalid duplicate handler name:"
+                                        handler-name)))
+                           (if (list? (cadr kws))
+                               (cadr kws)
+                               (list (cadr kws)))))
+            (loop (cddr kws) reversed-interfaces exports re-exports))
            ((#:export #:export-syntax)
             (or (pair? (cdr kws))
                 (unrecognized kws))
 ;; to change scm_c_use_module as well.
 
 (define (process-use-modules module-interface-args)
-  (for-each (lambda (mif-args)
-             (let ((mod-iface (apply resolve-interface mif-args)))
-               (or mod-iface
-                   (error "no such module" mif-args))
-               (module-use! (current-module) mod-iface)))
-           module-interface-args))
+  (module-use-interfaces! (current-module)
+                         (map (lambda (mif-args)
+                                (or (apply resolve-interface mif-args)
+                                    (error "no such module" mif-args)))
+                              module-interface-args)))
 
 (defmacro use-modules modules
   `(eval-case
      (process-use-modules
       (list ,@(map (lambda (m)
                     `(list ,@(compile-interface-spec m)))
-                  modules))))
+                  modules)))
+     *unspecified*)
     (else
      (error "use-modules can only be used at the top level"))))
 
 (define load load-module)
 
 \f
+;;; {Handling of duplicate imported bindings}
+;;;
+
+;; Duplicate handlers take the following arguments:
+;;
+;; module  importing module
+;; name           conflicting name
+;; int1           old interface where name occurs
+;; val1           value of binding in old interface
+;; int2           new interface where name occurs
+;; val2           value of binding in new interface
+;; var    previous resolution or #f
+;; val    value of previous resolution
+;;
+;; A duplicate handler can take three alternative actions:
+;;
+;; 1. return #f => leave responsibility to next handler
+;; 2. exit with an error
+;; 3. return a variable resolving the conflict
+;;
+
+(define duplicate-handlers
+  (let ((m (make-module 7)))
+    (set-module-name! m 'duplicate-handlers)
+    (set-module-kind! m 'interface)
+    (module-define! m 'check
+     (lambda (module name int1 val1 int2 val2 var val)
+       (scm-error 'misc-error
+                 #f
+                 "module ~A: duplicate binding ~A imported from ~A and ~A"
+                 (list (module-name module)
+                       name
+                       (module-name int1)
+                       (module-name int2))
+                 #f)))
+    (module-define! m 'first
+     (lambda (module name int1 val1 int2 val2 var val)
+       (or var (module-local-variable int1 name))))
+    (module-define! m 'last
+     (lambda (module name int1 val1 int2 val2 var val)
+       (module-local-variable int2 name)))
+    m))
+
+(define (make-duplicates-interface)
+  (let ((m (make-module)))
+    (set-module-kind! m 'interface)
+    (set-module-name! m 'duplicates)
+    m))
+
+(define (module-symbol-interface module sym)
+  (or-map (lambda (interface)
+           (module-search (lambda (interface sym)
+                            (and (module-local-variable interface sym)
+                                 interface))
+                          interface
+                          sym))
+         (module-uses module)))
+
+(define (process-duplicates module interface)
+  (let* ((duplicates-info (module-duplicates-info module))
+        (handlers (car duplicates-info))
+        (d-interface (cdr duplicates-info)))
+    (module-for-each
+     (lambda (name var)
+       (let ((prev-interface (module-symbol-interface module name)))
+        (if prev-interface
+            (begin
+              (if (not d-interface)
+                  (begin
+                    (set! d-interface (make-duplicates-interface))
+                    (set-cdr! duplicates-info d-interface)))
+              (let* ((var (module-local-variable d-interface name))
+                     (val (and var (variable-bound? var) (variable-ref var))))
+                (let loop ((handlers handlers))
+                  (cond ((null? handlers))
+                        (((car handlers)
+                          module
+                          name
+                          prev-interface
+                          (module-symbol-local-binding prev-interface name #f)
+                          interface
+                          (module-symbol-local-binding interface name #f)
+                          var
+                          val)
+                         =>
+                         (lambda (var)
+                           (module-add! d-interface name var)))
+                        (else
+                         (loop (cdr handlers))))))))))
+     interface)))
+
+\f
 
 ;;; {`cond-expand' for SRFI-0 support.}
 ;;;