Ticket Created
over 2 years ago

CIQQA-1133

Inconsistent type checker errors

With strict type checking:

import Toybox.Application;
enum Foo {
    Zero,
    One,
    Two,
    Three,
}
function foo(x as Foo) as Void {}
function bar() as Void {
    foo(0);           // Fails, as expected
    foo(Zero);        // Works, as expected
    foo(Zero as Foo); // Works, as expected
    foo(0 as Foo);    // Works, as expected
    Storage.setValue("foo", Zero);          // Works
    Storage.setValue("foo", 0);             // Works
    Storage.setValue("bar", Zero as Foo);   // Fails (why?)
    Storage.setValue("baz", 0 as Foo);      // Fails (why?)
}

The error from Storage.setValue is "Invalid '$.Foo' passed as parameter 2 of type 'PolyType<Null or $.Toybox.Application.PropertyKeyType or $.Toybox.Lang.Array<$.Toybox.Application.PropertyValueType> or $.Toybox.Lang.Dictionary<$.Toybox.Application.PropertyKeyType,$.Toybox.Application.PropertyValueType> or $.Toybox.WatchUi.BitmapResource>'."

The first four lines of bar show that "Foo" is its own type, and that both Numbers and Foos can be passed when cast to Foo. Storage.setValue is happy to accept a Zero, so it seems like it accepts type Foo. But it *won't* accept 'Zero as Foo', or '0 as Foo'. Whats going on? It should either reject Zero (and require "Zero as Number" instead), or accept Zero, "Zero as Foo" and "0 as Foo".

Parents
  • Ok. But my enum *doesn't* have any strings in it.

    But the real point is this: suppose I want to mechanically replace every occurrence of Zero in my program with its actual value, so `0` in this case, but I want to keep things type-correct so the type checker will still report issues (in particular, it will report issues that I've just introduced).

    If I replace Zero with 0, then calls to foo fail. If I replace it with (0 as Foo), then calls to Storage.setValue fail. Of course, I could also analyze the context, and figure out which one is needed - but that means writing my own type checker, and mimicking all the weirdness in yours (and yes, this really is weirdness). And don't forget it could have been:

    var x = Zero;
    ...
    foo(x);
    Storage.setValue("foo", x);

    Now what do I replace x's initializer with? `0` will result in an error from foo, and `0 as Foo` will result in an error from Storage.setValue.

    But your whole point seems forced. If the compiler "knows that Zero is really a number", why does it not "know that 0 as Foo is really a number"? Also, why is the typecheker varying its behavior based on the value of an object, rather than its type?

Comment
  • Ok. But my enum *doesn't* have any strings in it.

    But the real point is this: suppose I want to mechanically replace every occurrence of Zero in my program with its actual value, so `0` in this case, but I want to keep things type-correct so the type checker will still report issues (in particular, it will report issues that I've just introduced).

    If I replace Zero with 0, then calls to foo fail. If I replace it with (0 as Foo), then calls to Storage.setValue fail. Of course, I could also analyze the context, and figure out which one is needed - but that means writing my own type checker, and mimicking all the weirdness in yours (and yes, this really is weirdness). And don't forget it could have been:

    var x = Zero;
    ...
    foo(x);
    Storage.setValue("foo", x);

    Now what do I replace x's initializer with? `0` will result in an error from foo, and `0 as Foo` will result in an error from Storage.setValue.

    But your whole point seems forced. If the compiler "knows that Zero is really a number", why does it not "know that 0 as Foo is really a number"? Also, why is the typecheker varying its behavior based on the value of an object, rather than its type?

Children
No Data