cljs-dev

ClojureScript compiler & std lib dev, https://clojurescript.org/community/dev
2019-08-11T14:55:24.058500Z

Why does this code

(defn f [a]
  (let [ret a]
    (fn [])))
is being emitted with a closure around returning function?
cljs.user.f = (function cljs$user$f(a){
  var ret = a;
  return ((function (ret){
    return (function (){
      return null;
    });
    ;})(ret))
});
Does it have something to do with local binding?

mfikes 2019-08-11T15:06:31.059100Z

Hey @dnolen, a regression surrounding the .call / ^js revision: https://clojure.atlassian.net/browse/CLJS-3156

mfikes 2019-08-11T15:09:08.060400Z

@roman01la Yeah, I suppose if you ask "What strategy does ClojureScript use to implement locally-scoped bindings for let?" the answer is that it uses anonymous function closures to achieve that end.

mfikes 2019-08-11T15:10:40.060900Z

I think you are asking a deeper question, though, about the enclosed value...

2019-08-11T15:12:29.062100Z

I think I’m trying to understand why is it emitted in this case where it seems like there’s no point in additional scope. But I guess this is just a general strategy and cljs doesn’t do any optimizations.

mfikes 2019-08-11T15:15:23.064600Z

Oh, right. It is definitely true that the compiler often employs simple strategies that work generally, and oftentimes doesn't attempt to optimize for specific cases (relying on Closure). And oftentimes, code-gen optimizations that have been introduced are there simply because Closure or the underlying JavaScript VMs are not clever enough to optimize the emitted code. I know the above is not really saying much, but, its a true statement. 🙂

mfikes 2019-08-11T15:17:11.066100Z

The simplest example of this notion is the generous emission of parentheses in the JavaScript. Honestly, no real effort is made to determine when they can be elided. And oftentimes we find we need to throw in extra parens to ensure the desired semantics for corner cases that are discovered.

mfikes 2019-08-11T15:27:22.067100Z

@roman01la With the patch in https://clojure.atlassian.net/browse/CLJS-3077, here is what is generated instead:

cljs.user.f = (function cljs$user$f(a){
  var ret = a;
  return (function (){
    return null;
  });
});

dnolen 2019-08-11T22:41:42.068200Z

@roman01la fns are loop/recur contexts and before we didn't know what we were going to see because only one pass

dnolen 2019-08-11T22:42:18.068900Z

since we compile loop/recur to JS loops you get mutable locals

👍 2
dnolen 2019-08-11T22:42:40.069400Z

only way to avoid that is that close over the environment and pass the values of all locals

dnolen 2019-08-11T22:44:27.070100Z

@mfikes I'm somewhat skeptical about 3156 - since we are using the function tag to determine if call can be eliminated

dnolen 2019-08-11T22:44:39.070400Z

not the arguments - but your example is about tagging args

dnolen 2019-08-11T22:45:41.071200Z

I mean the Canary test failed so something got caught - but it doesn't add up to me at the moment

mfikes 2019-08-11T22:46:13.071900Z

Yeah, perhaps I'm mischaracterizing the root cause. All I really have in the ticket is a :sha where it worked and the next :sha where it broke

dnolen 2019-08-11T22:47:05.072500Z

@mfikes is Canary run w/ optimizations or no?

mfikes 2019-08-11T22:47:24.073Z

I'll check... it is the Hoplon build that it failed in.

dnolen 2019-08-11T22:47:49.073400Z

if so I'd be even more skeptical since there wouldn't be call or not anyway

dnolen 2019-08-11T22:47:52.073600Z

direct fn method invoke

mfikes 2019-08-11T22:49:01.074800Z

FWIW, the generated JavaScript ends up looking roughly like new PersistentHashSet(... its args)(the other args) IIRC

dnolen 2019-08-11T22:50:23.075900Z

that would imply that PersistentHashSet is being tagged js

mfikes 2019-08-11T22:50:29.076Z

Here is a quick example that is probably less sketchy

new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, 1, ["node",null], null), null).call(null,some_value_here)

mfikes 2019-08-11T22:50:58.076600Z

This example reflects good code gen. I think the .call gets removed and breaks it.

mfikes 2019-08-11T22:53:43.077300Z

The code generated for the failing example in the ticket is:

new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, 1, ["node",null], null), null)(require("process").title)

mfikes 2019-08-11T22:54:58.077800Z

The code generated for the previous :sha is

new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, 1, ["node",null], null), null).call(null,require("process").title);

dnolen 2019-08-11T22:56:15.078300Z

@mfikes I'm also a bit confused because - there was commit where I backed out that change

dnolen 2019-08-11T22:56:25.078800Z

that code no longer exists in master - there's something a bit different now

mfikes 2019-08-11T22:57:00.079600Z

Oh... OK, perhaps my git bisect randomly went down that path. Hah. Crap.

dnolen 2019-08-11T22:57:34.080Z

I just mean we cannot infer that's the breaking thing because it doesn't even exist anymore

dnolen 2019-08-11T22:57:55.080600Z

so we know that commit is bad

mfikes 2019-08-11T22:58:07.080900Z

OK. That ticket's invalid then. I'll cook up a new one based on how Hoplon is breaking with master.

dnolen 2019-08-11T22:58:32.081500Z

reflects what I wanted to do w/ master

dnolen 2019-08-11T22:59:00.081700Z

this commit widened the scope https://github.com/clojure/clojurescript/commit/8f38049d543b04b8da55029f140b6577e3ec245a

dnolen 2019-08-11T22:59:10.082Z

so I'd be curious to know what code gen looks like w/ those commits

mfikes 2019-08-11T23:00:47.082600Z

I confirmed that https://clojure.atlassian.net/browse/CLJS-3156 doesn't occur with the master :sha

dnolen 2019-08-11T23:03:06.082900Z

@mfikes so Canary isn't borked?

mfikes 2019-08-11T23:03:22.083100Z

Hoplon breaks with master

dnolen 2019-08-11T23:03:30.083500Z

ok cool, so something else entirely

mfikes 2019-08-11T23:03:44.083900Z

So, I did a git bisect, and accidentally ended up in the path of that interim broken commit.

dnolen 2019-08-11T23:03:56.084400Z

(well not cool - but at least we know that's not the culprit)

mfikes 2019-08-11T23:04:03.084600Z

I'm going to do another bisect to see if I can isolate it