Skip to content

Conversation

@shantanu-sardesai
Copy link
Contributor

@shantanu-sardesai shantanu-sardesai commented Nov 1, 2025

Description

We should begin analyzing the metadata as well. At present, we store the metadata source directly without proper analysis or evaluation, resulting in the following code executing despite being semantically incorrect:

user=> (def ^{:pp #(println foo)} bar 0)
#'user/bar

user=> (meta bar)
nil

user=> (meta #'bar)
{:jank/source {:file "/var/folders/xd/9v1dcg0d46l7d19830vq0lhc0000gn/T/jank-repl-fYkERF", :start {:offset 27, :line 1, :col 28}, :end {:offset 30, :line 1, :col 31}}, :pp (fn* [] (println foo))}

user=> ((-> #'bar meta :pp))
Uncaught exception: Invalid call with 0 args to: (fn* [] (println foo))

There are two issues being highlighted here:

  1. Although the foo symbol is not in scope during the definition of the bar var, jank does not emit any analysis failure warnings.
  2. Due to lack of evaluation currently the :pp key in the metadata of the bar var contains the source code instead of a compiled function, preventing us from invoking the function.

Goal

Within the scope of this PR I wish to start analyzing the metadata such that the above cases behave as would be expected:

user=> (def ^{:pp #(println foo)} bar 0)
─ analyze/unresolved-symbol ────────────────────────────────────────────────────────────────────────
error: Unable to resolve symbol 'foo'.

─────┬──────────────────────────────────────────────────────────────────────────────────────────────
     │ /var/folders/xd/9v1dcg0d46l7d19830vq0lhc0000gn/T/jank-repl-hKkVT2
─────┼──────────────────────────────────────────────────────────────────────────────────────────────
  1  │ (def ^{:pp #(println foo)} bar 0)
     │                      ^^^ Found here.
─────┴──────────────────────────────────────────────────────────────────────────────────────────────

user=> (def foo 0)
#'user/foo

user=> (def ^{:pp #(println foo)} bar 0)
#'user/bar

user=> (meta #'bar)
{:jank/source {:file "/var/folders/xd/9v1dcg0d46l7d19830vq0lhc0000gn/T/jank-repl-hKkVT2", :start {:offset 27, :line 1, :col 28}, :end {:offset 30, :line 1, :col 31}}, :pp #object [user/user-fn-53 jit_function 0x10ec383f8]}

user=> ((-> #'bar meta :pp))
0
nil

Fixes

Resolves #197.

@shantanu-sardesai
Copy link
Contributor Author

Currently the following doesn't work:

(let* [foo 42]
  (def ^{:pp #(println foo)} bar 0)
  (assert (= ((-> #'bar meta :pp)) 42)))

But before I dive deeper I thought best align on where to store the analysis results for meta.

Copy link
Member

@jeaye jeaye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start, Shantanu. Your overall unwrap usage is lacking a lot of necessary discipline. The testing strategy will need some more detailed planning, too, since this opens up a lot of doors. Finally, IR gen has not been updated, so none of this will work when wrapped in a function or AOT compiled.

(assert (= one 'one))
(assert (= (:doc (meta #'one)) nil))

; Analysis with global variables
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not how new tests should be added. Each test should have a dedicated test file, describing what it's testing. Also, we now have possibilities for extra errors, which come from analyzing meta. That needs to be tested.

{
auto var(__rt_ctx->intern_var(expr->name).expect_ok());
var->meta = expr->name->meta;
var->meta = some(eval(expr->meta.unwrap()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is lazy and not safe (because we don't know that meta contains a value). Look at line 281 for not only the variable you should use, but how to safely grab this value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've chosen to wrap the unwrap in an is_some block so that I won't even bother calling eval when there is no meta available.


; Analysis with global variables
(def foo 42)
(def ^{:pp #(println foo)} bar 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail if you wrap it in a function or let*, since IR gen has not been updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

def meta should be evaluated and support arbitrary values

2 participants