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 !
Parents
  • I think the issue you are running into is the compiler cannot infer all possible types of RESOLUTION_MAP.
    This seems to be a compiler limitation as it does not know the scope of usage.

    - Vars Inside of a function, the compiler can guarantee scope, and thus is able to infer types (usually correctly)

    *public* infers the Dictionary is accessible outside of the Unit, and therefore, it doesn't *know" if it can support other types.  Declaring as 'private' might help. (bizarrely const declaration does not help)

    - In strongly-typed languages compilers can fully resolve the types for this.  But Monkey C is "Monkey-typed" -- it has no limits on re-typing.

    I find if you want the compiler to *know* the types outside of a function, you often have to *tell* it explicitly to only accept a given type (basically a compiler hint): 

    as Dictionary<Lang.Number, Lang.Array<Lang.Symbol>>;

    e.g.

    public static const RESOLUTION_MAP = {

            1   => [:_4K, :_16R9],
            4   => [:_2K7, :_16R9],
            6   => [:_2K7, :_4R3],
            7   => [:_1440, :_16R9],
            9   => [:_1080, :_16R9],
    } as Dictionary<Lang.Number, Lang.Array<Lang.Symbol>>;
Comment
  • I think the issue you are running into is the compiler cannot infer all possible types of RESOLUTION_MAP.
    This seems to be a compiler limitation as it does not know the scope of usage.

    - Vars Inside of a function, the compiler can guarantee scope, and thus is able to infer types (usually correctly)

    *public* infers the Dictionary is accessible outside of the Unit, and therefore, it doesn't *know" if it can support other types.  Declaring as 'private' might help. (bizarrely const declaration does not help)

    - In strongly-typed languages compilers can fully resolve the types for this.  But Monkey C is "Monkey-typed" -- it has no limits on re-typing.

    I find if you want the compiler to *know* the types outside of a function, you often have to *tell* it explicitly to only accept a given type (basically a compiler hint): 

    as Dictionary<Lang.Number, Lang.Array<Lang.Symbol>>;

    e.g.

    public static const RESOLUTION_MAP = {

            1   => [:_4K, :_16R9],
            4   => [:_2K7, :_16R9],
            6   => [:_2K7, :_4R3],
            7   => [:_1440, :_16R9],
            9   => [:_1080, :_16R9],
    } as Dictionary<Lang.Number, Lang.Array<Lang.Symbol>>;
Children
No Data