surprising behavior involving computed field name, +:, and super

247 views
Skip to first unread message

Shimin Guo

unread,
Jul 25, 2017, 4:15:17 PM7/25/17
to jso...@googlegroups.com
First of all, let me acknowledge that I ask this question mostly to satisfy my curiosity. It's not likely to arise in practical code.

With that, I find this behavior surprising:

$ jsonnet -e '{[{a: "y"}{b: super.a}.b]+: 1}'
STATIC ERROR:  Can't use self outside of an object.

The expression desugars (jsonnet fmt --debug-desugaring -e ...) to

local $outer_self = self, $outer_super_index0 = super["a"];
{
   [{
       a: local $ = self; "y",
   } + {
       b: local $ = self; super["a"],
   }.b]:
       if {
           a: local $ = $outer_self; "y",
       } + {
           b: local $ = $outer_self; $outer_super_index0,
       }.b in super then
           super[{
               a: local $ = $outer_self; "y",
           } + {
               b: local $ = $outer_self; $outer_super_index0,
           }.b] +
           local $ = self; 1
       else
           local $ = self; 1,
}

Looks like when the [e]+:f is desugared to [e]: super[???] + f, in ???, super.a is evaluated in the context outside the current object. Is this an intentional behavior? Why should super.a in this case evaluate to anything other than "y"?

Dave Cunningham

unread,
Jul 25, 2017, 5:01:23 PM7/25/17
to Shimin Guo, Jsonnet
You have found a bug in the desugarer.  It is in logic that is intended to solve the following problem:

Correct execution:
$ jsonnet -e '{ foo:: "field_name", bar: { [self.foo] +: "field_value" } }'
{
   "bar": {
      "field_name": "field_value"
   }
}

Naive desugaring:
$ jsonnet -e '{ foo:: "field_name", bar: { [self.foo] : super[self.foo] + "field_value" } }'
RUNTIME ERROR: Field does not exist: foo

Actual desugaring (simplified for clarity):
{
    foo:: "field_name",
    bar: local $outer_self = self; {
        [self.foo]: super[$outer_self.foo] + "field_value"
    },
}

Except in your case it has gone too far and hoisted it out of a deeper object when a) it could have left it in place, b) moving it has changed the behavior.  The bug is in desugaring.cpp in the definition of the SubstituteSelfSuper pass, which does not stop at a new object subtree.

As a work around, you can hoist out the local manually, which will avoid this desugaring logic being triggered:

local foo = {a: "y"}{b: super.a}.b
{[foo]+: 1}


Note that the spec is correct but actually quite vague because it refers to e[$outerself/self,$outersuper/superbefore defining what [ ] actually means.  We should also fix that by using the definition of capture avoiding substitution that is defined further down the spec.

--
You received this message because you are subscribed to the Google Groups "Jsonnet" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jsonnet+unsubscribe@googlegroups.com.
To post to this group, send email to jso...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jsonnet/CAOBFCeo67hbEtR9_%2BnvaPUEFcSFJ%2B_%2Bb%2BCW-DRX6XGG0r81Nmw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages