Hi,

When using tools.analyzer.jvm you have to remember that its primary
use-case is as a front-end for a compiler, in this case :tag and :o-tag
serve to inform tools.emitter.jvm when to emit a cast thus might not
always reflect the :tag meta of the expression :form.

Regarding the :tag/:o-tag difference, :o-tag (you can read it as
"original tag") holds the static type of the node known at that point
and might be either inferred by the :tag of some children nodes or from
the class of the :form of the node in case it's a literal, :tag on the
other hand can be either the same of :o-tag or hold the Class that node
needs to be cast to, usually because of an explicit type-hint.

For example, ^IPersistentCollection [] will have :o-tag PersistentVector
and :tag IPersistentCollection.

:class is an attribute of some nodes that deal with host interop forms,
like :new, :instance-call and others and holds the Class that node deals
with; for example it might hold the Class a :new node is going to
instantiate, the Class an :instance-method belongs to etc.

BTW, you don't need to roll your own `analyze` function providing those
bindings, jvm/analyze already sets up the right bindings for you.

I hope this helps,
Nicola



On Wed, May 21, 2014 at 10:00 AM, guns <s...@sungpae.com> wrote:

> Hello,
>
> This question is primarily addressed to Nicola Mometto, but I am sending
> it to the list so it may be helpful to other Clojure developers. Help is
> appreciated, regardless of the source.
>
> I am using tools.analyzer(.jvm) to build a Closeable resource linter,
> but I have run into the following problem: a function call's type hint
> is lost when an instance method is called on its output.
>
> For example, given the following setup:
>
>     (alias 'ana 'clojure.tools.analyzer)
>     (alias 'ast 'clojure.tools.analyzer.ast)
>     (alias 'jvm 'clojure.tools.analyzer.jvm)
>
>     (defn analyze [form]
>       (binding [ana/macroexpand-1 jvm/macroexpand-1
>                 ana/create-var    jvm/create-var
>                 ana/parse         jvm/parse
>                 ana/var?          var?]
>         (jvm/analyze form (jvm/empty-env))))
>
>     (defn analyze-debug [form]
>       (for [ast (ast/nodes (analyze form))
>             :let [{:keys [op form tag o-tag class]} ast]]
>         (array-map :op op :form form :tag tag :o-tag o-tag :class class)))
>
>     (defn ^java.io.FileInputStream fis [^String x]
>       (java.io.FileInputStream. x))
>
> I would like to detect that (fis x) returns a java.io.FileInputStream.
>
> If I call:
>
>     (analyze-debug '(str (fis "x")))
>
> I receive:
>
>     ({:op :invoke,
>       :form (str (fis "x")),
>       :tag java.lang.String,
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :var,
>       :form str,
>       :tag clojure.lang.AFunction,
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :invoke,
>       :form (fis "x"),
>       :tag java.io.FileInputStream, ; ◀──── The desired metadata
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :var,
>       :form fis,
>       :tag clojure.lang.AFunction,
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :const,
>       :form "x",
>       :tag java.lang.String,
>       :o-tag java.lang.String,
>       :class nil})
>
> However, if I call:
>
>     (analyze-debug '(.toString (fis "x")))
>     ->
>     ({:op :instance-call,
>       :form (. (fis "x") toString),
>       :tag java.lang.String,
>       :o-tag java.lang.String,
>       :class java.lang.Object}
>      {:op :invoke,
>       :form (fis "x"),
>       :tag java.lang.Object, ; ◀──── The type hint is missing!
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :var,
>       :form fis,
>       :tag clojure.lang.AFunction,
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :const,
>       :form "x",
>       :tag java.lang.String,
>       :o-tag java.lang.String,
>       :class nil})
>
> The :tag of (fis "x") is now java.lang.Object, and
> java.io.FileInputStream is not present in the node.
>
> Calling java.io.InputStream#markSupported sheds more light on the
> matter:
>
>     (analyze-debug '(.markSupported (fis "x")))
>     ->
>     ({:op :instance-call,
>       :form (. (fis "x") markSupported),
>       :tag boolean,
>       :o-tag boolean,
>       :class java.io.InputStream}
>      {:op :invoke,
>       :form (fis "x"),
>       :tag java.io.InputStream, ; ◀──── The instance method's class
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :var,
>       :form fis,
>       :tag clojure.lang.AFunction,
>       :o-tag java.lang.Object,
>       :class nil}
>      {:op :const,
>       :form "x",
>       :tag java.lang.String,
>       :o-tag java.lang.String,
>       :class nil})
>
> Finally, calling java.io.FileInputStream#getFD on (fis "x") returns the
> expected :tag entry:
>
>     (analyze-debug '(.getFD (fis "x")))
>     ->
>     (…
>      {:op :invoke,
>       :form (fis "x"),
>       :tag java.io.FileInputStream, ; ◀──── Also the instance method's
> class
>       :o-tag java.lang.Object,
>       :class nil}
>      …)
>
> Is there a reliable way to retrieve the original type hint of
> (.method (fis "x"))?
>
> If not, does it make sense to ensure that the metadata of the (fis "x")
> node is constant regardless of its context?
>
> And finally, while we're on the topic, what is the difference between
> the :tag, :o-tag, and :class entries? I have a vague idea based on a
> quick perusal of the library, but I would be delighted if you could
> provide a quick summary.
>
> Thank you for your hard work on these analyzers! You are laying the
> groundwork for a whole new generation of Clojure tools.
>
>     guns
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to