Acknowledged

sdk-6.2.2 changes lookup rules for class statics

This code:

module MyMod {
    const ModConst = 2;
    class Base {
        static const BaseConst = 3;
    }
}

class Test extends MyMod.Base {
    function initialize() {
        Base.initialize();
    }
    static const TestOne = ENDIAN_BIG;
    static const TestTwo = ModConst;
    static const TestThree = BaseConst;
}

function test() {
  System.println(Test.TestOne);
  System.println(Test.TestTwo);
  System.println(Test.TestThree);
}

Compiles (even with Strict type checking) and runs as you might expect prior to sdk-6.2.2. ie

  • TestOne is assigned the value of Lang.ENDIAN_BIG (it finds it by following the inheritance chain to MyMod.Base, then Lang.Object, and then searches out from Lang.Object to find Lang.ENDIAN_BIG).
  • TestTwo is assigned the value of MyMod.ModConst (again, it follows the inheritance chain to MyMod.Base, and then searches out to MyMod)
  • TestThree is assigned the value of Base.BaseConst.

With the default optimization level, you can see (via -g) that the values have been substituted at compile time, so no runtime lookups actually happen. But if you set optimization level to 0, no substitutions happen, and it's clear that all the lookups happen at runtime, and work.

With sdk-6.2.2 the type checker complains that ENDIAN_BIG, ModConst and BaseConst don't exist, and refuses to compile. If you turn the type checker off, it then compiles, but crashes at runtime because the symbols don't exist (and I've verified by testing each of the three cases separately that all of them fail).

So the runtime has been changed, and the type checker has been updated to match - making me think this is an intentional change. But it breaks existing code, and its not clear what it might fix...

Also note that similar code without "static" continues to work.

Parents
  • What's static vs non static is supposed to mean for const?

    answered this.

    Actually while reading the code I wasn't sure if it should work. I would expect BaseConst to work, but ModConst? Test isn't in MyMod, so why would it work?

    I've posted about this before - but a cursory search didn't find it. Inside a non-static class method, the search order is:

    • Class Members, then
    • base class Members, then
    • ... same for all super classes, then
    • Object's class members, then
    • All modules containing the class (outwards), then
    • All modules containing the base class (outwards), then
    • ... same for all super classes, then
    • All modules containing Object... so Lang, then Toybox, then $ (although $ would have already been searched)

    So in this case, it looks up ModConst in Test, and fails, so it looks for it in Base, and fails, so it looks for it in Object, and fails; then it looks for it in $ (because thats the only module containing Test) and fails, then it looks for it in MyMod (because thats the module containing Base) and finds it.

    For ENDIAN_BIG, it does the same, but fails to find it in MyMod or $, so goes on to looking in Lang, and finds it (no imports required).

    Note that these rules generally only apply to non-static methods (or more accurately to methods that are called on an instance - because static methods can be called on an instance, and non-static methods can be called statically, and the rules depend on how the method is called, not on how its declared).

    Prior to 6.2.2, those same rules were also used in class member initializers, whether they were static members or not. But as of sdk-6.2.2 static member initializers follow the lookup rules used in statically called methods (and perhaps thats why they changed the rules... to be "consistent").

Comment
  • What's static vs non static is supposed to mean for const?

    answered this.

    Actually while reading the code I wasn't sure if it should work. I would expect BaseConst to work, but ModConst? Test isn't in MyMod, so why would it work?

    I've posted about this before - but a cursory search didn't find it. Inside a non-static class method, the search order is:

    • Class Members, then
    • base class Members, then
    • ... same for all super classes, then
    • Object's class members, then
    • All modules containing the class (outwards), then
    • All modules containing the base class (outwards), then
    • ... same for all super classes, then
    • All modules containing Object... so Lang, then Toybox, then $ (although $ would have already been searched)

    So in this case, it looks up ModConst in Test, and fails, so it looks for it in Base, and fails, so it looks for it in Object, and fails; then it looks for it in $ (because thats the only module containing Test) and fails, then it looks for it in MyMod (because thats the module containing Base) and finds it.

    For ENDIAN_BIG, it does the same, but fails to find it in MyMod or $, so goes on to looking in Lang, and finds it (no imports required).

    Note that these rules generally only apply to non-static methods (or more accurately to methods that are called on an instance - because static methods can be called on an instance, and non-static methods can be called statically, and the rules depend on how the method is called, not on how its declared).

    Prior to 6.2.2, those same rules were also used in class member initializers, whether they were static members or not. But as of sdk-6.2.2 static member initializers follow the lookup rules used in statically called methods (and perhaps thats why they changed the rules... to be "consistent").

Children
No Data