Ticket Created
over 2 years ago

CIQQA-1357

The type checker's symbol lookup doesn't match the runtime.

I originally posted some of this as a question about how lookup was supposed to work, but now I can see it's not just unexpected behavior, it's actually buggy.

function foo() as Number {
    return 0;
}

module bar {
    module foo {
        module bar {
            const foo = 1;
            const bar = 2;
            const buz = 3;
            function baz() as Number {
                return foo;
            }
        }
    }
}

ERROR: <device>: bug.mc:12: Object of type '$.bar.foo' does not match return type '$.Toybox.Lang.Number'.

But if I turn the type checker off, or put "return foo as Number;" in there so that it compiles, the code runs as expected, and returns 1. So the type checker is looking up foo incorrectly (ie it doesn't match what the runtime actually does, or what users are likely to expect).

On the other hand, if I add "using Toybox.Application as foo;" at line 4, then the type checker still gives the same error (ie it still thinks that baz will return $.bar.foo), but the runtime actually returns $.Toybox.Application. And if I add "using bar.foo;" at line 4, it actually *does* return $.bar.foo.

So the type checker's lookup rule seems to be:

Look outwards for a module with the given name, use it if found, otherwise look outwards for a non-module.

But the runtime's lookup rule seems to be:

Look outwards for an imported/used module with the given name, use it if found, otherwise look outwards for the first declaration with the given name.

At the very least, the type checker should match the runtime behavior...

Edit: It seems the type checker's rules change if you use foo.x instead of foo. But they're still incorrect:

//using bar as foo;
module bar {
    const x = "Hello";
    module foo {
        const x = 3;
        module bar {
            const foo = 1;
            function baz() as Number {
                return foo.x;
            }
        }
    }
}

Given the new code, the type checker is happy, and thinks baz is returning bar.foo.x. But in fact it's returning 1.x (ie the const foo), which fails at runtime (this matches its behavior for "foo" in the first example).

If I uncomment line 1, the type checker complains that baz is returning a string. If I turn off the typechecker, and run it, it baz does indeed return "Hello". So now the type checker really does match the runtime behavior wrt using.

If I go back to "return foo;", the type checker complains "Object of type '$.bar.foo' does not match return" whether line 1 is commented out or not, where the runtime returns $.foo.bar when its commented out, and $.bar when its not.

  • The other part of my original question was about function lookup.

    Again, when the type checker is looking up a function, it looks outwards, ignoring any matching symbols that aren't actually functions. So if I change line 12 of the first example in this post to "return foo();", the type checker is happy, and thinks that its calling $.foo(), even though there's a const foo, and a module foo which look like better candidates. And in fact the runtime *does* pick const foo as the thing to be called (and fails, obviously).

    Also, it appears that when looking up functions, the runtime *doesn't* prioritize "using" names, the way it does for non-function lookups - so it would find "const foo" even if there was a using foo in an outer scope.