Acknowledged
CIQQA-3490

Compilator wrongly assuming 'Attempting to perform container access on null.'

Hey,

I'm currently developing a Connect IQ app and I think I found a bug in the MonkeyC compilator. With the following code, the second line in the onStart method would be underlined with the error  'Attempting to perform container access on null.'. Trying to compile would result in the same issue. This is performed from the default widget template app with VSCode, SDK version 8.2.3 and trying to compile for device forerunner 955.

import Toybox.Application;
import Toybox.Lang;
import Toybox.WatchUi;
import Toybox.System;

class testApp extends Application.AppBase {
    public static const RESOLUTION_MAP = {
        1   => [:_4K, :_16R9],
        4   => [:_2K7, :_16R9],
        6   => [:_2K7, :_4R3],
        7   => [:_1440, :_16R9],
        9   => [:_1080, :_16R9],
    };

    function initialize() {
        AppBase.initialize();
    }

    // onStart() is called on application start up
    function onStart(state as Dictionary?) as Void {
        var test = 1;
        var result = RESOLUTION_MAP.get(test)[0];
        System.println(result);
    }

    // onStop() is called when your application is exiting
    function onStop(state as Dictionary?) as Void {
    }

    // Return the initial view of your application here
    function getInitialView() as [Views] or [Views, InputDelegates] {
        return [ new testView() ];
    }

}

function getApp() as testApp {
    return Application.getApp() as testApp;
}
I found a workaround by type checking the container access as followed so that I can compile my code, but I still get a compilation warning for the line within the if statement 'Statement is not reachable'. I checked in a test project, and the statement is actually reachable if the key provided exists in the const Dictionary RESOLUTION_MAP.
var resolution = RESOLUTION_MAP.get(setting);
if (resolution instanceof Symbol) {
    System.println(RESOLUTION_LABELS.get(resolution[0]));
}

Don't hesitate to reach out to me if you need any other information to reproduce the issue !
  • More details
    (*) of course contents of the dictionary can be changed (keys can be added and removed, and the associated values can be changed). It's just that RESOLUTION_MAP itself cannot be reassigned to a different value. It is also possible to add/set values of a different type than already exists in the dictionary.
    e.g. the following code will compile
    RESOLUTION_MAP.put(1, [:_1440, :_16R9]) // change value for existing key
    RESOLUTION_MAP.set(2,[:_1440, :_16R9]) // add key
    RESOLUTION_MAP.remove(4); // remove key
    This means:
    - I was wrong before when I said that a get() of a "known" key should produce VALUE_TYPE
    - Actually, the compiler should infer Object or Null in every case, as it's not possible to infer the type in this case
     
    [3/x]
  • > This should've been posted to the discussion forum, not to the bug report...

    There's arguably several bugs here, as I said earlier, unless I am missing something;
    - accessing the dictionary via get() with a variable that may or may not exist as a key [at init time] produces Null when it should arguably produce VALUE_TYPE or Null [or actually, Object or Null, because the contents of the dictionary can be changed *]
    - accessing the dictionary via [ ] with a variable that may or may not exist as a key [at init time] produces Any when it should arguably produce VALUE_TYPE or Null [or actually Object or Null *]
    - accessing the dictionary via get() with a constant key that's known to exist [at init time *] produces VALUE_TYPE or Null when it should be Object or Null 
    - Even though RESOLUTION_MAP is typed as having the exact keys and value types it has on init, nothing prevents the dev from adding/removing keys or changing values. Which means that the inferred type of RESOLUTION_MAP is wrong, unless there is a way to prevent keys from being added or removed, or at the very least to prevent existing keys from being changed. I understand that this is more of a limitation, but it kind of relates to the previous points
    I understand these are limitations of the type system and the compiler is actually trying to be helpful here, but maybe due these limitations it would be more correct for get[] / [ ] to always return Object or Null, instead of the compiler variously inferring Null, VALUE_TYPE or Null, or Any
    [1/2]
  • This should've been posted to the discussion forum, not to the bug report...

  • Yep, I actually meant "instanceof Array", I fixed that just after filing the bug report. Checking if resolution is not null is not enough for the compiler to allow using the variable for container access afterwards.