Ticket Created
over 3 years ago

CIQQA-1387

Change in runtime behavior in 4.1.4 Compiler2 Beta

Given this code:

import Toybox.Test;
import Toybox.Lang;

module LocalResolution {
    module AX {
        module BX {
            const K = 1;
        }
    }
    import LocalResolution.AX.BX;
    module CX {
        module BX {
            const K = 1001;
        }
        class X {
            const K = 2001;
        }
        function foo() as Number {
            var BX = new X();

            return BX.K; // should be X.K
        }
    }
    (:test)
    function testImport(logger as Logger) as Boolean {
        var k = CX.foo();
        if (k != 2001) {
            logger.debug("Oops - found the wrong k: " + k.toString());
            return false;
        }
        return true;
    }
}

The test passes with compilers up to 4.1.4 (non-beta). With Compiler2 Beta, it fails, because foo() returns 1.

Without the import, it works. But with the import, even though BX is a local variable, BX.K resolves to LocalResolution.AX.BX.K.

The *typechecker* in earlier versions of the compiler thought this is how things worked. But the runtime disagreed (and I've reported that as a type checker bug previously). Apparently, in this release, you've changed the runtime to match the type checker (I guess it can't really be the runtime; the compiler must be transforming BX into something more explicit, like $.LocalResolution.AX.BX. Or maybe its optimizing the constant away, even without a -O flag). The net result though, is that the runtime behavior has changed.

This seems like a serious mistake. If adding an import can cause an existing local variable to resolve to the import instead, all hell breaks loose. The import could be at global scope, in a different file, for example.

Parents
  • In fact, this starts to get insane. Inside the function foo, the plain symbol BX resolves as the local, but BX.anything resolves as the module. So if I add "const Y = 1;" to class X, and then do:

    ...
            function foo() as Number {
                var BX = new X();
                if (BX has :Y) {
                    return BX.Y;
                }
                return BX.K;
            }
    ...

    The BX in 'BX has :Y' inspects the local variable BX for a Y, and finds it. Then the return BX.Y resolves BX to the module, and crashes.

    In fact, the typechecker does complain about this one (so you have to disable it to make it run)... but I hope this makes it clear that you really can't override local variables from outside the function (and even outside the file where they're defined).

Comment
  • In fact, this starts to get insane. Inside the function foo, the plain symbol BX resolves as the local, but BX.anything resolves as the module. So if I add "const Y = 1;" to class X, and then do:

    ...
            function foo() as Number {
                var BX = new X();
                if (BX has :Y) {
                    return BX.Y;
                }
                return BX.K;
            }
    ...

    The BX in 'BX has :Y' inspects the local variable BX for a Y, and finds it. Then the return BX.Y resolves BX to the module, and crashes.

    In fact, the typechecker does complain about this one (so you have to disable it to make it run)... but I hope this makes it clear that you really can't override local variables from outside the function (and even outside the file where they're defined).

Children