unofficial mirror of bug-guile@gnu.org 
 help / color / mirror / Atom feed
* Quite sneaky bug in SRFI-9
@ 2011-03-10  0:23 Andreas Rottmann
  2011-03-11 13:55 ` Ludovic Courtès
  0 siblings, 1 reply; 5+ messages in thread
From: Andreas Rottmann @ 2011-03-10  0:23 UTC (permalink / raw)
  To: Guile Bugs

[-- Attachment #1: Type: text/plain, Size: 1951 bytes --]

Hi!   After being puzzled for some time, I found the root cause 
for an issue with my code was actually a quite serious bug in 
SRFI-9's `define-inlinable' (here we go again -- but this time 
it's an actual bug, I promise ;-)):  The current implementation of 
`define-inlinable' duplicates actual parameters passed to the 
macros it generates, for example (note the `(find-next-solution! s 
10000)' expression):  scheme@(guile-user)> (tree-il->scheme 
(macroexpand '(solution? (find-next-solution! s 10000))))
$6 = (if ((@@ (srfi srfi-9) struct?) (find-next-solution! s 10000))
         ((@@ (srfi srfi-9) eq?) 
              ((@@ (srfi srfi-9) struct-vtable) (find-next-solution! s 10000))
              (@@ (dorodango solver) solution))
          #f)


Attached is a patch to fix this (along with a test case), but I guess
it's not as good as it could be:

- It doesn't preserve the current error-at-syntax-time behavior when an
  argument count mismatch occurs (this makes the first two "constructor"
  testcases in srfi-9.test fail, and obsolete).  I don't know if this is
  considered important.

- It's certainly not as efficient as the current, broken implementation.
  Ideally, the expansion would be a `let' block instead of the
  `call-with-values' invocation.  I'd guess the former might be more
  amendable for optimization, but perhaps I'm wrong here.  I didn't
  implement the solution using `let', as I believe it cannot be done in
  a straightforward way (i.e. by exploiting the pattern matcher only)
  with Guile's current syntax-case implementation.

Has anyone done benchmarks of the benefits of the current (broken)
SRFI-9 accessors and predicates vs. regular procedures on real code?
Given the argument duplication issue, it might be the case that
`define-inlinable' actually slowed things down :-).

Perhaps removing `define-inlinable' entirely would be an acceptable
alternative fix for this issue?

Anyway, here's the patch:



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: srfi-9-fix2.diff --]
[-- Type: text/x-diff, Size: 2561 bytes --]

From: Andreas Rottmann <a.rottmann@gmx.at>
Subject: Fix argument duplication `define-inlinable' in SRFI-9

* modules/srfi/srfi-9.scm (define-inlinable): Don't duplicate argument
  expressions in the expansion of the macros generated.
* test-suite/tests/srfi-9.test (constructor): Adapted.
  (side-effecting arguments): New test for the argument duplication
  issue.

---
 module/srfi/srfi-9.scm       |    5 +++--
 test-suite/tests/srfi-9.test |   22 +++++++++-------------
 2 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/module/srfi/srfi-9.scm b/module/srfi/srfi-9.scm
index fad570b..34dbb53 100644
--- a/module/srfi/srfi-9.scm
+++ b/module/srfi/srfi-9.scm
@@ -87,8 +87,9 @@
              (define-syntax name
                (lambda (x)
                  (syntax-case x ()
-                   ((_ formals ...)
-                    #'(begin body ...))
+                   ((_ . arguments)
+                    #'(call-with-values (lambda () (values . arguments))
+                        (lambda (formals ...) body ...)))
                    (_
                     (identifier? x)
                     #'proc-name))))))))))
diff --git a/test-suite/tests/srfi-9.test b/test-suite/tests/srfi-9.test
index f8006c4..6766482 100644
--- a/test-suite/tests/srfi-9.test
+++ b/test-suite/tests/srfi-9.test
@@ -38,21 +38,10 @@
 
 (with-test-prefix "constructor"
 
-  ;; Constructors are defined using `define-integrable', meaning that direct
-  ;; calls as in `(make-foo)' lead to a compile-time psyntax error, hence the
-  ;; distinction below.
-
-  (pass-if-exception "foo 0 args (inline)" exception:syntax-pattern-unmatched
-     (compile '(make-foo) #:env (current-module)))
-  (pass-if-exception "foo 2 args (inline)" exception:syntax-pattern-unmatched
-     (compile '(make-foo 1 2) #:env (current-module)))
-
   (pass-if-exception "foo 0 args" exception:wrong-num-args
-     (let ((make-foo make-foo))
-       (make-foo)))
+    (make-foo))
   (pass-if-exception "foo 2 args" exception:wrong-num-args
-     (let ((make-foo make-foo))
-       (make-foo 1 2))))
+    (make-foo 1 2)))
 
 (with-test-prefix "predicate"
 
@@ -94,6 +83,13 @@
   (pass-if-exception "set-y! on bar" exception:wrong-type-arg
      (set-y! b 99)))
 
+(with-test-prefix "side-effecting arguments"
+
+    (pass-if "predicate"
+      (let ((x 0))
+        (and (foo? (begin (set! x (+ x 1)) f))
+             (= x 1)))))
+
 (with-test-prefix "non-toplevel"
     
   (define-record-type :frotz (make-frotz a b) frotz?
-- 
tg: (df12979..) t/srfi-9-fix2 (depends on: stable-2.0)

[-- Attachment #3: Type: text/plain, Size: 63 bytes --]


Regards, Rotty
-- 
Andreas Rottmann -- <http://rotty.yi.org/>

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: Quite sneaky bug in SRFI-9
  2011-03-10  0:23 Quite sneaky bug in SRFI-9 Andreas Rottmann
@ 2011-03-11 13:55 ` Ludovic Courtès
  2011-03-11 19:28   ` Andy Wingo
  2011-03-11 19:30   ` Andy Wingo
  0 siblings, 2 replies; 5+ messages in thread
From: Ludovic Courtès @ 2011-03-11 13:55 UTC (permalink / raw)
  To: bug-guile

[-- Attachment #1: Type: text/plain, Size: 1079 bytes --]

Hi Andreas,

Andreas Rottmann <a.rottmann@gmx.at> writes:

> Hi!   After being puzzled for some time, I found the root cause for an
> issue with my code was actually a quite serious bug in SRFI-9's
> define-inlinable' (here we go again -- but this time it's an actual
> bug, I promise ;-)):  The current implementation of `define-inlinable'
> duplicates actual parameters passed to the macros it generates, for
> example (note the `(find-next-solution! s 10000)' expression):
> scheme@(guile-user)> (tree-il->scheme (macroexpand '(solution?
> (find-next-solution! s 10000))))
> $6 = (if ((@@ (srfi srfi-9) struct?) (find-next-solution! s 10000))
>         ((@@ (srfi srfi-9) eq?)            ((@@ (srfi srfi-9)
> struct-vtable) (find-next-solution! s 10000))
>              (@@ (dorodango solver) solution))
>          #f)
>

Ouch, good catch!

> - It's certainly not as efficient as the current, broken implementation.
>  Ideally, the expansion would be a `let' block instead of the
>  `call-with-values' invocation.

Yes, that would be nice.  This patch seems to do the trick:


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: Type: text/x-patch, Size: 1365 bytes --]

diff --git a/module/srfi/srfi-9.scm b/module/srfi/srfi-9.scm
index fad570b..54bc8a8 100644
--- a/module/srfi/srfi-9.scm
+++ b/module/srfi/srfi-9.scm
@@ -64,6 +64,12 @@
 
 (cond-expand-provide (current-module) '(srfi-9))
 
+(define-syntax letify
+  (syntax-rules ()
+    ((_ (var ...) (val ...) body ...)
+     (let ((var val) ...)
+       body ...))))
+
 (define-syntax define-inlinable
   ;; Define a macro and a procedure such that direct calls are inlined, via
   ;; the macro expansion, whereas references in non-call contexts refer to
@@ -80,15 +86,17 @@
     (syntax-case x ()
       ((_ (name formals ...) body ...)
        (identifier? #'name)
-       (with-syntax ((proc-name (make-procedure-name #'name)))
+       (with-syntax ((proc-name  (make-procedure-name #'name))
+                     ((args ...) (generate-temporaries #'(formals ...))))
          #`(begin
              (define (proc-name formals ...)
                body ...)
              (define-syntax name
                (lambda (x)
                  (syntax-case x ()
-                   ((_ formals ...)
-                    #'(begin body ...))
+                   ((_ args ...)
+                    #'(letify (formals ...) (args ...)
+                              (begin body ...)))
                    (_
                     (identifier? x)
                     #'proc-name))))))))))

[-- Attachment #3: Type: text/plain, Size: 794 bytes --]


I can’t think of any way to do it more elegantly.

> Has anyone done benchmarks of the benefits of the current (broken)
> SRFI-9 accessors and predicates vs. regular procedures on real code?
> Given the argument duplication issue, it might be the case that
> `define-inlinable' actually slowed things down :-).

I benchmarked it indirectly via ‘vlist-cons’ et al. in ‘vlists.bm’.

The bonus is that there are special opcodes for ‘struct-vtable’,
‘make-struct’, and ‘struct-ref’.  So when you inline constructors and
accessors, you also get to use directly those opcodes, which makes quite
a difference.

I would like to keep this property.  Not doing this gives an incentive
to using lists everywhere since ‘car’, ‘pair?’, etc. are open-coded.

Thanks!

Ludo’.

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: Quite sneaky bug in SRFI-9
  2011-03-11 13:55 ` Ludovic Courtès
@ 2011-03-11 19:28   ` Andy Wingo
  2011-03-11 19:30   ` Andy Wingo
  1 sibling, 0 replies; 5+ messages in thread
From: Andy Wingo @ 2011-03-11 19:28 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: bug-guile

On Fri 11 Mar 2011 14:55, ludo@gnu.org (Ludovic Courtès) writes:

>>  Ideally, the expansion would be a `let' block instead of the
>>  `call-with-values' invocation.
>
> Yes, that would be nice.  This patch seems to do the trick:

Looks good to me.  Want to apply?

Andy
-- 
http://wingolog.org/



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Quite sneaky bug in SRFI-9
  2011-03-11 13:55 ` Ludovic Courtès
  2011-03-11 19:28   ` Andy Wingo
@ 2011-03-11 19:30   ` Andy Wingo
  2011-03-11 20:03     ` Ludovic Courtès
  1 sibling, 1 reply; 5+ messages in thread
From: Andy Wingo @ 2011-03-11 19:30 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: bug-guile

Hi,

On Fri 11 Mar 2011 14:55, ludo@gnu.org (Ludovic Courtès) writes:

>               (define-syntax name
>                 (lambda (x)
>                   (syntax-case x ()
> -                   ((_ formals ...)
> -                    #'(begin body ...))
> +                   ((_ args ...)
> +                    #'(letify (formals ...) (args ...)
> +                              (begin body ...)))

Alternately:

  ((_ args ...)
   #'((lambda (formals ...)
        body ...)
      args ...))

Guile will turn that into a let.

Andy
-- 
http://wingolog.org/



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Quite sneaky bug in SRFI-9
  2011-03-11 19:30   ` Andy Wingo
@ 2011-03-11 20:03     ` Ludovic Courtès
  0 siblings, 0 replies; 5+ messages in thread
From: Ludovic Courtès @ 2011-03-11 20:03 UTC (permalink / raw)
  To: Andy Wingo; +Cc: bug-guile

Hi!

Andy Wingo <wingo@pobox.com> writes:

> On Fri 11 Mar 2011 14:55, ludo@gnu.org (Ludovic Courtès) writes:
>
>>               (define-syntax name
>>                 (lambda (x)
>>                   (syntax-case x ()
>> -                   ((_ formals ...)
>> -                    #'(begin body ...))
>> +                   ((_ args ...)
>> +                    #'(letify (formals ...) (args ...)
>> +                              (begin body ...)))
>
> Alternately:
>
>   ((_ args ...)
>    #'((lambda (formals ...)
>         body ...)
>       args ...))
>
> Guile will turn that into a let.

Nice!

I pushed this and Andreas’ test case, thank you!

Ludo’.



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-03-11 20:03 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-10  0:23 Quite sneaky bug in SRFI-9 Andreas Rottmann
2011-03-11 13:55 ` Ludovic Courtès
2011-03-11 19:28   ` Andy Wingo
2011-03-11 19:30   ` Andy Wingo
2011-03-11 20:03     ` Ludovic Courtès

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).