Acknowledged

Type checker does not detect access of non-existent symbol on class definition when class is assigned to a variable

SDK: 7.4.3

Type check level: any non-0 level, including the default level

Consider the following (invalid) code:

var pressure = Sensor.Info.pressure;

As expected, this produces a type check error:

Cannot find symbol ':pressure' on class definition '$.Toybox.Sensor.Info'

However, if Sensor.Info is (incorrectly) assigned to an intermediate variable and the same lookup is attempted, the type checker says nothing.

var info = Sensor.Info;
var pressure2 = info.pressure;

At runtime, pressure2 will be null. It could be argued that info.pressure is an invalid lookup (same as Sensor.Info.pressure), and that perhaps info = Sensor.info doesn't make any sense, either. Is it valid to assign a class to a variable like that, so it can be presumably passed around to other functions? This doesn't seem to be a common or valid Monkey C coding pattern.

For example, the following code produces a type check error as expected:

function test() {
  foo("42"); // Invalid '$.Toybox.Lang.String' passed as parameter 1 of type '$.Toybox.Lang.Number'
}

function foo(x as Number) {
  System.println(x); // prints "42" if the type checker is disabled
}

But the following code produces no type check error, even though surely a class type is not compatible with a Number type:

function test() {
  var info = System.Info;
  foo(info);
}

function foo(x as Number) {
  System.println(x); // prints "class"
}

And the following code crashes at runtime, which is further evidence that it doesn't make sense to pass classes around in Monkey C

function test() {
  foo(System.Info); // crashes at runtime [*]
}

function foo(x as Number) {
  System.println(x);
}

[*] Crashes with error message:

Error: Symbol Not Found Error
Details: Could not find symbol 'Info'

  • Note that this bug also applies when the symbol which doesn't exist on the class definition also doesn't exist on the class instance (but does exist as a defined symbol, somewhere).

    For example (compile the test cases separately):

    // Test case 1

    var pressure1 = Sensor.Info.pressure; // error: "Cannot find symbol ':pressure' on class definition '$.Toybox.Sensor.Info'."
    var info = Sensor.Info;
    var pressure2 = info.pressure; // no type check error (pressure exists on the Sensor.Info instance, not class)
    var pressure3 = info.println; // no type check error (println does not exist on the Sensor.Info instance or class, but is a defined symbol)

    // Test case 2

    var info = Sensor.Info;
    var pressure4 = info.undefinedSymbol; // error: "Undefined symbol ':undefinedSymbol' detected" (undefinedSymbol is not defined anywhere in the app or SDK)

    (Test case 2 has to be commented out in order to see any errors from the other test case, since any undefined symbol errors will cause compilation to halt before other types of errors can be found.)

  • As a side note, it seems that the following anti-pattern is not unheard of for users who are unfamiliar with OOP (I've seen it more than once in the forums):

    var info = Sensor.Info;
    var pressure = info.pressure;
    System.println(pressure); // prints "null", new dev doesn't understand why

    So it would be nice if the type checker could nip this kind of problem in the bud.