Kawa is a Scheme implementation that runs on Java Virtual machine, much like Jython, Jruby, Clojure, Scala to name a few. This year I wanted to apply for GSoC and I approached Kawa community. I talked to the maintainer of Kawa, Per Bothner. I asked for a task to familiarize myself with the Kawa repo (to show my skills that I am capable of contributing?). I was asked to add a new compiler flag, --strict-typing. Mr. Bothner gave me a hint, he told that I should look into checkType method in InlineCalls.java.

This my first time trying to contribute to a free software code base that I did not author. Normally I would’ve started with the compiler’s docs, but I could not find docs that I thought was helpful. I took a deep dive into the code base trying to read the checkType method. I was not able to make much off of it. Then I tried to read the source of the objects in InlineCalls.java. I also tried to read the super class of the InlineCalls. I was grepping around to find some thing that I was capable of understanding. I think I failed on my face for I was not able to understand the method that I was supposed to make a change on.

Reading other’s code has always been a daunting task to me, I changed my approach. So I grepped for some compiler flags, --inline-calls actually. Then for the sake of fun I just grepped public static void main, why void? Just for the sake for fun, I guess. Then I found repl.java. All the command-line arguments were processed here, so this is the starting point (I’ve discovered the door). I added my --strict-typing flag there and I observed the pattern, then --inline-calls set a flag Compilation.inlineOk, so I added my new flag to Compilation.java. After this I was trying to study --inline-calls flag. I tried to guess where it went from repl.java. So I just started including print statements that printed to STDERR. I just kept going up and up and I lost track. So I created an Exception there and printed the Trace and continued execution. Now I got a beautiful trace of the control flow. So now this was excellent way to find the control flow (may be I should use a debugger?). Now I started playing around inlineCalls.java with a custom test-file that I wrote. I invoked Kawa interpreter with --inline-calls flag. Then I started poking around the testsuite to find tests that inline calls. Now I got files that will surely trigger the inlining related functions. Again I was trying to print tracebacks from functions that I thought was reachable. Probably I should call printing trace backs as break points from now on.

Now Including a break point in checkType would be useful to understand the control flow. This was what I got:

java.lang.Exception: Reached here?
at gnu.expr.InlineCalls.checkType(InlineCalls.java:88)
at gnu.expr.InlineCalls.visit(InlineCalls.java:83)
at gnu.expr.InlineCalls.visitApplyExp(InlineCalls.java:188)
at gnu.expr.InlineCalls.visitApplyExp(InlineCalls.java:43)
at gnu.expr.ApplyExp.visit(ApplyExp.java:534)
at gnu.expr.ExpVisitor.visit(ExpVisitor.java:56)
at gnu.expr.InlineCalls.visit(InlineCalls.java:69)
at gnu.expr.InlineCalls.visit(InlineCalls.java:43)
at gnu.expr.LambdaExp.visitChildrenOnly(LambdaExp.java:1733)
at gnu.expr.LambdaExp.visitChildren(LambdaExp.java:1722)
at gnu.expr.InlineCalls.visitScopeExp(InlineCalls.java:562)
at gnu.expr.InlineCalls.visitLambdaExp(InlineCalls.java:739)
at gnu.expr.InlineCalls.visitLambdaExp(InlineCalls.java:43)
at gnu.expr.ExpVisitor.visitModuleExp(ExpVisitor.java:101)
at gnu.expr.InlineCalls.visitModuleExp(InlineCalls.java:603)
at gnu.expr.InlineCalls.visitModuleExp(InlineCalls.java:43)
at gnu.expr.ModuleExp.visit(ModuleExp.java:446)
at gnu.expr.ExpVisitor.visit(ExpVisitor.java:52)
at gnu.expr.InlineCalls.visit(InlineCalls.java:69)
at gnu.expr.InlineCalls.inlineCalls(InlineCalls.java:53)
at gnu.expr.Compilation.process(Compilation.java:1903)
at gnu.expr.ModuleInfo.loadByStages(ModuleInfo.java:306)
at gnu.expr.ModuleExp.evalModule1(ModuleExp.java:231)
at kawa.Shell.compileSource(Shell.java:587)
at kawa.Shell.runFile(Shell.java:555)
at kawa.Shell.runFileOrClass(Shell.java:468)
at kawa.repl.processArgs(repl.java:702)
at kawa.repl.main(repl.java:822)

I explored, it was easy given the line numbers. The next thing I did was to print the types when the checkType method was reached.

Take these two examples:

;;; strict-types.scm
(define (greater-equal x::int y::int)::boolean
(>= x y))
;;; not-so-strict.scm
(define (greater-equal x y)
(>= x y))

for strict-types.scm, I printed the Type of each Expression that is passed to checkType method, here it goes:

Type void
Type class-type
ClassType gnu.kawa.functions.Convert
ClassType gnu.kawa.lispexpr.LangPrimType
ClassType gnu.kawa.functions.ApplyToArgs
ClassType gnu.kawa.functions.NumberCompare
Type int
Type int
Type boolean
Type boolean
ClassType gnu.bytecode.PrimType
ClassType gnu.bytecode.PrimType
ClassType gnu.mapping.Procedure
ClassType strictType

and for not-so-strict-types.scm, I printed the Type, here it goes:

Type void
Type class-type
ClassType gnu.kawa.functions.ApplyToArgs
ClassType gnu.kawa.functions.NumberCompare
ClassType java.lang.Object
ClassType java.lang.Object
Type boolean
ClassType gnu.bytecode.ClassType
ClassType gnu.bytecode.ClassType
ClassType gnu.mapping.Procedure
ClassType strictType

What can we infer from the dump of types that we got above? Implementing strict types is not Trivial. The InlineCalls.checkType() method is not only invoked for checking the arguments and return types but for functions, operators, lambda expressions and more.

But we are concerned about the parameters and the return types only.

Check this out (Solution?):

if (Compilation.strictTyping && exp.getName().equals("java.lang.Object")) {
throw new Error("Useful error message");
}

Here we do not distinguish between the return type and the arguments. We plainly filter the java.lang.Object. This ensures that generics cannot be used. I think this does ensure strict typing.