From: Lars Rune Nøstdal
Subject: THE to function as DECLARE?
Date: 
Message-ID: <a95c113b-8bef-459a-974e-3a5d3a4f09e6@l5g2000vbp.googlegroups.com>
;;; This is SBCL specific, but I think most implementations
;;;  have the CLTL2 stuff somewhere.
(defmacro lex-type-info (name &environment env)
  (declare (ignorable env))
  `(cdr (assoc 'type (third (multiple-value-list
                             (sb-cltl2:variable-information
                              ,name env))))))


(defun test (x)
  (format t "T: ~S~%" x))

(defun test-number (x)
  (format t "NUMBER: ~S~%" x))

(defun test-string (x)
  (format t "STRING: ~S~%" x))

(define-compiler-macro test (&whole form arg &environment env)
  (declare (ignorable arg))
  (let ((lex-type (lex-type-info arg)))
    ;;(format t "LEX-TYPE: ~A~%" lex-type)
    (if (eq nil lex-type)
        form
        (cond
          ((subtypep lex-type 'number) `(test-number ,arg))
          ((subtypep lex-type 'string) `(test-string ,arg))
          (t form)))))

(defun do-test ()
  (list
   (let ((x 1))
     (test x) ;; T: 1
     x)

   (let ((x (the fixnum 2)))
     (test x) ;; T: 2
     x)

   (let ((x 3))
     (declare (fixnum x))
     (test x) ;; NUMBER: 3
     x)

   (let ((x "a"))
     (test x) ;; T: "a"
     x)

   (let ((x (the string "b")))
     (test x) ;; T: "b"
     x)

   (let ((x "c"))
     (declare (string x))
     (test x) ;; STRING: "c"
     x)))



;;; What currently happens is:
#|
  (do-test)
  T: 1
  T: 2
  NUMBER: 3
  T: "a"
  T: "b"
  STRING: "c"
|#


;;; ..but would the following be possible seeing as nothing in the LET
forms
;;; change the variable?

#|
  (do-test)
  NUMBER: 1
  NUMBER: 2
  NUMBER: 3
  STRING: "a"
  STRING: "b"
  STRING: "c"
|#


Is this even possible or valid wrt. the spec?

From: Lars Rune Nøstdal
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <44e4083f-b770-4968-a719-1f37e94cc196@r16g2000vbn.googlegroups.com>
Ups, that should have been:

  (defmacro lex-type-info (name &optional env)
    `(cdr (assoc 'type (third (multiple-value-list
                               (sb-cltl2:variable-information
                                ,name ,(or env sb-c:*lexenv*)))))))


..and..


  ...
  (let ((lex-type (lex-type-info arg env)))
  ...


..I think.
From: Lars Rune Nøstdal
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <f05c05d2-c249-4107-a6b0-04a16eec7210@g20g2000vba.googlegroups.com>
This seems to work:

(defmacro lex-type-info (name &optional env)
  (once-only (name)
    (with-gensyms (lambda-var)
      (let ((env (or env sb-c:*lexenv*)))
        `(let ((sb-c:*lexenv* ,env))
           (if-let ((,lambda-var (sb-c:lexenv-find ,name vars)))
             (sb-kernel:type-specifier (sb-c::lambda-var-type ,lambda-
var))
             nil))))))



AMX> (let ((x 42))
       (dbg-princ (lex-type-info 'x))
       x)
(LEX-TYPE-INFO 'X) => (INTEGER 42 42)
42


;;; With the patch to /src/pcl/ctor.lisp below applied:

AMX> (defclass test () ())
#<STANDARD-CLASS TEST>

AMX> (let ((x (make-instance 'test)))
       (dbg-princ (lex-type-info 'x))
       x)
(LEX-TYPE-INFO 'X) => TEST
#<TEST {BB85851}>


AMX> (let ((x 42))
       (dbg-princ (lex-type-info 'x))
       (incf x)
       x)
(LEX-TYPE-INFO 'X) => (INTEGER 42 43)
43


AMX> (let ((x 42))
       (dbg-princ (lex-type-info 'x))
       (setf x "hello")
       x)
(LEX-TYPE-INFO 'X) => (OR (INTEGER 42 42) (SIMPLE-ARRAY CHARACTER
(5)))
"hello"
AMX>


..this is kinda cool. :)


For the MAKE-INSTANCE return value type to propagate, this is needed
(Google will mess this up, but anyway):


diff --git a/src/pcl/ctor.lisp b/src/pcl/ctor.lisp
index aebc884..7cffa64 100644
--- a/src/pcl/ctor.lisp
+++ b/src/pcl/ctor.lisp
@@ -226,7 +226,9 @@
                                     t)
                           (function (&rest t) t))
                       ,function-name))
-              (funcall (function ,function-name) ,@value-
forms))))))))
+              (truly-the (values ,class-name &optional) (funcall
(function ,function-name) ,@value-forms))
+              ;;(funcall (function ,function-name) ,@value-forms)
+              )))))))
From: Vassil Nikolov
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <snz63ejod3d.fsf@luna.vassil.nikolov.name>
In

  (let ((x <expression>))
    (declare (type <type> x))
    ...)

we have, of course, declared the type of the variable X; the
consequences of the value of <expression> not being of that type are
undefined.  It may or may not be possible to determine that at compile
time.

On the other hand, in

  (let ((x (the <type> <expression>)))
    ...)

we have not declared anything about the type of the variable X, but
only what the type of its initial value is.  The language processor is
not obliged to infer anything about the type of X from that; and if it
does, the result will depend on what else is happening to X (as well
as on the implementation, of course).  For example, in

  (let ((x (the fixnum (g y))))
    (h x)
    (setq x (the double-float (u z)))
    (v x)
    (setq x (w s))
    (mu x))

(OR FIXNUM DOUBLE-FLOAT) would have to be a subtype of the inferred
type of X, which will also depend on what is known about W's return
type and the type of MU's argument.

By the way, the THE form is redundant in (THE FIXNUM 2).  Something
like (THE FIXNUM (+ I J)) would be more interesting.

---Vassil.


-- 
Vassil Nikolov <········@pobox.com>

  (1) M(Gauss);
  (2) M(a) if M(b) and declared(b, M(a)),
  where M(x) := "x is a mathematician".
From: Lars Rune Nøstdal
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <0028a759-9b0d-4827-a851-fd9602927f38@p23g2000vbl.googlegroups.com>
On Jun 26, 6:04 am, Vassil Nikolov <········@pobox.com> wrote:
> In
>
>   (let ((x <expression>))
>     (declare (type <type> x))
>     ...)
>
> we have, of course, declared the type of the variable X; the
> consequences of the value of <expression> not being of that type are
> undefined.  It may or may not be possible to determine that at compile
> time.
>
> On the other hand, in
>
>   (let ((x (the <type> <expression>)))
>     ...)
>
> we have not declared anything about the type of the variable X, but
> only what the type of its initial value is.  The language processor is
> not obliged to infer anything about the type of X from that; and if it
> does, the result will depend on what else is happening to X (as well
> as on the implementation, of course).

Yeah, I'm trying to get to the type information the compiler is
finding. If I've understood how this works (or is supposed to work?),
it will "never" infer too optimistic or specific types -- at least not
"on its own". So if it can't be sure at all, it'll fall back to T. At
least I think that's what will and is supposed happen. :)


> For example, in
>
>   (let ((x (the fixnum (g y))))
>     (h x)
>     (setq x (the double-float (u z)))
>     (v x)
>     (setq x (w s))
>     (mu x))
>
> (OR FIXNUM DOUBLE-FLOAT) would have to be a subtype of the inferred
> type of X, which will also depend on what is known about W's return
> type and the type of MU's argument.
>
> By the way, the THE form is redundant in (THE FIXNUM 2).  Something
> like (THE FIXNUM (+ I J)) would be more interesting.

Yeah, or perhaps:

AMX> (declaim (inline square))
; No value
AMX> (defun square (x)
       (declare (fixnum x))
       (the fixnum (* x x)))
SQUARE
AMX> (fun-info 'square)
(FUNCTION (FIXNUM) (VALUES (UNSIGNED-BYTE 29) &OPTIONAL))
AMX> (let* ((x 2)
            (square-of-x (square x)))
       (dbg-prin1 (lex-var-info 'x))
       (dbg-prin1 (lex-var-info 'square-of-x))
       (values x square-of-x))
(LEX-VAR-INFO 'X) => (INTEGER 2 2)
(LEX-VAR-INFO 'SQUARE-OF-X) => (INTEGER 4 4)
2
4
AMX>


..anyway, this is nice; it's great being able to dispatch at compile-
time even when I don't feel like adding (declare ..)'s "manually".



LEX-VAR-INFO is similar to the LEX-TYPE-INFO I posted above:

(defmacro lex-info (name kind &optional env)
  `(when-let* ((sb-c:*lexenv* ,(or env sb-c:*lexenv*))
               (lex-info (sb-c:lexenv-find ,name ,kind)))
     (sb-kernel:type-specifier (sb-c::leaf-type lex-info))))

(defmacro lex-var-info (name &optional (env nil env-supplied-p))
  (if env-supplied-p
      `(lex-info ,name vars ,env)
      `(lex-info ,name vars)))

(defun fun-info (name)
  (sb-kernel:type-specifier (sb-c::info :function :type name)))

(defmacro dbg-prin1 (form &optional (stream t))
  `(format ,stream "~S => ~S~%" ',form ,form))
From: Vassil Nikolov
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <snzbpoamk58.fsf@luna.vassil.nikolov.name>
On Fri, 26 Jun 2009 06:11:39 -0700 (PDT), Lars Rune N�stdal <···········@gmail.com> said:
AMX> (defun square (x)
>        (declare (fixnum x))
>        (the fixnum (* x x)))

  By the way, the two declarations here are inconsistent, even though
  the programmer may know that this is benign.  Older dialects only
  had FIXNUM, but in Common Lisp you can do better, e.g.

    (defun square (x)
      ;; something like this must be true for (* X X) to be a fixnum:
      (declare (type (integer -9999 9999) x))
      (* x x))  ;hoping the compiler will infer the return type

  And again, examples where the initial values of local variables are
  not compile-time constants (but still of known subranges) are more
  interesting.

> ..anyway, this is nice; it's great being able to dispatch at compile-
> time even when I don't feel like adding (declare ..)'s "manually".

  Yes, it is great to have automatic type inference.  It's not only
  highly desirable for type declarations to be optional, but also to
  be largely unnecessary.

  ---Vassil.


-- 
Vassil Nikolov <········@pobox.com>

  (1) M(Gauss);
  (2) M(a) if M(b) and declared(b, M(a)),
  where M(x) := "x is a mathematician".
From: Lars Rune Nøstdal
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <18c3326d-5ebc-4891-995a-25af31fbadd6@n4g2000vba.googlegroups.com>
On Jun 27, 5:26 am, Vassil Nikolov <········@pobox.com> wrote:
> On Fri, 26 Jun 2009 06:11:39 -0700 (PDT), Lars Rune Nøstdal <···········@gmail.com> said:
> AMX> (defun square (x)
>
> >        (declare (fixnum x))
> >        (the fixnum (* x x)))
>
>   By the way, the two declarations here are inconsistent, even though
>   the programmer may know that this is benign.  Older dialects only
>   had FIXNUM, but in Common Lisp you can do better, e.g.
>
>     (defun square (x)
>       ;; something like this must be true for (* X X) to be a fixnum:
>       (declare (type (integer -9999 9999) x))
>       (* x x))  ;hoping the compiler will infer the return type


Hum, yeah. Can try that and see what happens:

AMX> (defun square (x)
       (declare ((integer -9999 9999) x))
       (* x x))
SQUARE
AMX> (fun-info 'square)
(FUNCTION ((INTEGER -9999 9999)) (VALUES (MOD 99980002) &OPTIONAL))
AMX>


>   And again, examples where the initial values of local variables are
>   not compile-time constants (but still of known subranges) are more
>   interesting.


Yeah, related to that, I think, (return value) types do not seem to
propagate unless SQUARE is inlined:

AMX> (progn
       (declaim (inline square))
       (defun square (x)
         (declare ((integer -9999 9999) x))
         (* x x))
       (declaim (notinline square)))

AMX> (fun-info 'square)
(FUNCTION ((INTEGER -9999 9999)) (VALUES (MOD 99980002) &OPTIONAL))

AMX> (let ((x (square 2)))
       (dbg-prin1 (lex-var-info 'x))
       x)
(LEX-VAR-INFO 'X) => T
4

AMX> (locally (declare (inline square))
       (let ((x (square 2)))
         (dbg-prin1 (lex-var-info 'x))
         x))
(LEX-VAR-INFO 'X) => (INTEGER 4 4)
4


I'm not sure if it's possible to do anything with that. ..and my main
goal was getting info about return value types wrt. MAKE-INSTANCE
calls anyway, so. :)


> > ..anyway, this is nice; it's great being able to dispatch at compile-
> > time even when I don't feel like adding (declare ..)'s "manually".
>
>   Yes, it is great to have automatic type inference.  It's not only
>   highly desirable for type declarations to be optional, but also to
>   be largely unnecessary.
>
>   ---Vassil.
>
> --
> Vassil Nikolov <········@pobox.com>
>
>   (1) M(Gauss);
>   (2) M(a) if M(b) and declared(b, M(a)),
>   where M(x) := "x is a mathematician".
From: Vassil Nikolov
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <snz8wjdlvil.fsf@luna.vassil.nikolov.name>
On Sat, 27 Jun 2009 04:57:10 -0700 (PDT), Lars Rune N�stdal <···········@gmail.com> said:
> ...
> (FUNCTION ((INTEGER -9999 9999)) (VALUES (MOD 99980002) &OPTIONAL))

  Of course, you can also (have your program to) check whether
  (SUBTYPEP '(MOD 99980002) #| or whatever the actual type is |# 'FIXNUM)
  whenever (SUBTYPEP <that type> 'INTEGER) is true.

  ---Vassil.


-- 
Vassil Nikolov <········@pobox.com>

  (1) M(Gauss);
  (2) M(a) if M(b) and declared(b, M(a)),
  where M(x) := "x is a mathematician".
From: Pascal J. Bourguignon
Subject: Re: THE to function as DECLARE?
Date: 
Message-ID: <87bpoaw27t.fsf@galatea.local>
Vassil Nikolov <········@pobox.com> writes:

> On Fri, 26 Jun 2009 06:11:39 -0700 (PDT), Lars Rune N�stdal <···········@gmail.com> said:
> AMX> (defun square (x)
>>        (declare (fixnum x))
>>        (the fixnum (* x x)))
>
>   By the way, the two declarations here are inconsistent, even though
>   the programmer may know that this is benign.  Older dialects only
>   had FIXNUM, but in Common Lisp you can do better, e.g.
>
>     (defun square (x)
>       ;; something like this must be true for (* X X) to be a fixnum:
>       (declare (type (integer -9999 9999) x))
>       (* x x))  ;hoping the compiler will infer the return type

Depending on the value of 'like'.  MOST-POSITIVE-FIXNUM may be as low as 32767, 
so (ISQRT MOST-POSITIVE-FIXNUM) may be as low as 181.

(defun square (x)
  (declare (type (integer #.(- (isqrt (- most-negative-fixnum)))
                          #.(isqrt most-positive-fixnum))))
  (the fixnum (* x x)))


-- 
__Pascal Bourguignon__