Skip to content

Compiler1 vs Compiler2 lookup rules

markw65 edited this page Jan 22, 2023 · 2 revisions

Garmin introduced a new compiler, "Compiler2" in version 4.1.6. This compiler has a better type checker, and better optimizations (enabled at -O2).

But it also introduced changes to the lookup rules for imported names. And just to complicate matters, even with Compiler2, the old rules apply at -O0, while the new rules apply at -O1 and -O2. [ Update: as of 4.2.0 beta 2, it looks like the new rules are used regardless of optimization level ]

Under the old rules, when you import a module via import, the name of the module and types the module declares become available to the type checker, but only the module name becomes available to code.

Under the new rules, the name of the module and types the module declares become available to the type checker (as before), but the module name and any modules or classes that it declares become available to code.

So

import Toybox.WatchUi

// Menu2 is a type in WatchUi, so is available to the type checker
// under the old and new rules, so you can just say 'Menu2' as
// the return type
function getMenu() as Menu2 {
  // runtime error under the old rules,
  // but works under the new.
  // The type checker thinks it works in both cases.
  return new Menu2({:title=>"My Menu2"});
}

But note that the type checker assumes the new rules apply in all cases, so it won't report an error at compile time. The code will crash at runtime, however.

There is yet-another complication. Inside non-static class methods (when invoked non-statically), all the names declared in all the containing modules of the class and any super classes it extends become available.

So for example all classes extend Object, which is declared in Lang, which is declared in Toybox. So inside a non-static method of a class, all Toybox modules are available (without any imports), and all Classes/Methods/Constants/Enums declared in Lang are available (again, without any imports).

// no imports
class MyClass {
  function wow(x) as Object {
    // Number is available because its declared in
    // Lang
    if (x instanceof Number) {
      return x;
    }
    // WatchUi is available, because its declared
    // in Toybox
    if (x instanceof WatchUi.Menu2) {
      return x;
    }
    // Application is available, because its declared
    // in Toybox.
    return Application.Properties.getValue("foo");
  }
}

But note that although "wow" looks like it could be static (it doesn't obviously use self anywhere), none of those symbols would be available in a static method. Or even if you invoke it statically (ie MyClass.wow(42) will crash at runtime).

Clone this wiki locally