Skip to content
This repository has been archived by the owner on Apr 28, 2020. It is now read-only.

Kind of static type checking? #4

Open
phmarek opened this issue Sep 16, 2016 · 16 comments
Open

Kind of static type checking? #4

phmarek opened this issue Sep 16, 2016 · 16 comments

Comments

@phmarek
Copy link

phmarek commented Sep 16, 2016

Hi,

first of all,

(let ((*features* (cons :inline-generic-function *features*)))
  (print (inline-generic-function '(plus a b))))

This symbol is not exported, and not even defined in my QL (inlined-generic-function-20160628-git).

Furthermore, I tried to get some static type checking. With a default method (that might be a default of inlined-generic-function even?) of

(defmethod plus (a b)
  (error "NO METHOD"))

I get variable defined but not used in call sites that might use that, but a more descriptive message would be better. Do you see any way to get a compile time error with a useful message?

Thank you very much!

@guicho271828
Copy link
Owner

sorry, there is a typo, try inline d - generic-function please! I'll fix it now.
Regarding static type checking I don't know if that can even possible in lisp nor it depends on which implementation are you using. Is that SBCL?

@guicho271828
Copy link
Owner

Oh, I was still wrong. The function was without d. This is not good ...

@guicho271828
Copy link
Owner

I also found that it looks like somwhere between SBCL 1.3.5 and 1.3.8 the unit-tests starts to fail. For example, tests for no-primari-method, invalid call-next-method, no-next-method and so on.

@guicho271828
Copy link
Owner

What a shame! the error was due to the change in assoc pattern semantics in trivia

This was referenced Sep 16, 2016
@guicho271828
Copy link
Owner

now both package/symbol export issue and unit-test issue is resolved.

@guicho271828
Copy link
Owner

We have a bad news and a good news.

I-G-F can signal no-primary-method, but this happens in runtime.
However, using an implementation-speific method, I partially made static type checking work on SBCL.
I don't think it is possible w/o implementation-specific features.

At first I tested what happens if I shadow a function with a local macro. However, branch pruning happens in IR of the implementation, thus normal macros/macrolets do not work. In order to modify the behavior depending on whether the branch is pruned or not, you should dive into IR1.

deftransform behaves like a macro and compiler-macro, but only when the code is reachable --- the unreachable dead-code is not transformed, as documented here.

@phmarek
Copy link
Author

phmarek commented Sep 17, 2016

Hi,

thanks a lot for the quick help!

An idea that I had during the night was to define some function (i-g-f:invalid-branch?) that has a compiler macro that throws an error; but at least on my SBCL that even gets used for valid calls, so the inlining seems to happen before code elimination. (As you describe above.)

IMO some implementation-specific magic is surely allowed, as this is a useful feature... so please, take the ir1-compile-time-error definition (with a bit more generic name?) and put it into i-g-f. I'd even like it as a default method (for all T types!), it can always be overridden per method again!

With that example it should be easy to get a few more solutions for other implementations on #lisp - and if not every one has a way to give compile-time-errors, then the runtime-error will have to be sufficient anyway.

Thank you!!

@guicho271828
Copy link
Owner

Using #+sbcl we can disable the deftransform on other impls. So we can guarantee that when it compiles on sbcl it is safe on other implementations as well.

@guicho271828
Copy link
Owner

but the saveat for this is that every method definitions should be already defined at compile-time. Thus I won't make it a default although I may provide a flag that automatically appends such an ir1-error.

@phmarek
Copy link
Author

phmarek commented Sep 17, 2016

Yeah.

The reason for some public name is to easily disallow some specific combinations:

(defmethod plus ((a number) (b number))
  (+ a b))

(defmethod plus ((a string) (b string))
  (concatenate 'string a b))

(defmethod plus ((a number) (b string))
  (inlined-g-f:invalid-branch))

(defmethod plus ((a string) (b number))
  (inlined-g-f:invalid-branch))

@phmarek
Copy link
Author

phmarek commented Sep 17, 2016

but the saveat for this is that every method definitions should be already defined at compile-time. Thus I won't make it a default although I may provide a flag that automatically appends such an ir1-error.
Yes, the methods would have to be defined before first use, at least.

BTW, is there a way to restrict the inlining to some maximum depth? In case a method wants to call another method (eg. after argument conversion) simply inlining again leads to a stack exhaustion...

@guicho271828
Copy link
Owner

@guicho271828
Copy link
Owner

So,

(defmethod a ...
  (declare (notinline a))
  ...
     (a ...))

should be able to avoid inifinite recursion.

@guicho271828
Copy link
Owner

i-g-f:invalid-branch sounds like a very good idea. However, it has a more appropriate place to be.
One candidate is to have a compatibility layer, named such as trivial-dead-code-elimination, or trivial-static-typing.
Another way of doing this is to include it as part of trivia and have a matcher variant such as strict-ematch, which fails to compile when the match could fail. (I-G-F can then switch from ematch to strict-ematch). What do you think?

@guicho271828
Copy link
Owner

Temporarily added INVALID-BRANCH and testing against various SBCL versions.

@phmarek
Copy link
Author

phmarek commented Sep 20, 2016

Thank you very much!

Yeah, having that in trivia sounds like a good idea, too.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants