Ticket Created
over 2 years ago

CIQQA-1383

New type checker doesn't allow comparisons between unknown types

I'm currently validating boolean config settings by comparing the result against true. If the config is a boolean (as it should be) its a no-op, it just converts false and true to themselves. If the config contains any other type (which actually seems to happen) it converts it to false.

so basically:

var sanitized = Application.Properties.getValue("foo") == true;

and the type checker errors out

Cannot perform operation 'eq' on types 'PolyType<Null or $.Toybox.Lang.Object>' and '$.Toybox.Lang.Boolean'.

This doesn't really make sense. You can compare any two types (and the runtime supports doing so). If their types are the same, then they might compare equal. Otherwise they shouldn't. I don't want to have to check "instanceof Boolean &&" for code size reasons. Its already a shame that I can't rely on Properties having their declared types, but I've seen instances where it happens, so now I validate everything.

  • I would say the new type checking is correct.

    I wouldn't. Here you go:

    (:test,:typecheck(false))
    function testBooleanOperations(logger as Logger) as Boolean {
        logger.debug("null == true: " + (null == true ? "true" : "false"));
        logger.debug("42 == true: " + (42 == true ? "true" : "false"));
        logger.debug("-1 == true: " + (-1 == true ? "true" : "false"));
        logger.debug("[] == true: " + ([] == true ? "true" : "false"));
        logger.debug("{} == true: " + ({} == true ? "true" : "false"));
        return true;
    }
    

    Executing test testBooleanOperations...
    DEBUG (07:50): null == true: false
    DEBUG (07:50): 42 == true: false
    DEBUG (07:50): -1 == true: false
    DEBUG (07:50): [] == true: false
    DEBUG (07:50): {} == true: false

    There is nothing incorrect about comparing null and true for equality. They're not equal. And there's nothing incorrect about comparing *any* two things for equality.

    I don't really understand your position on this. In every other discussion about type checking your position is "I don't use it much, because it forces me to go through contortions".

    Now you're saying I should write something that generates 7 times as much code, just to satisfy the type checker?

  • OTOH, I believe that the following code should *not* compile, because there's no overlap between between the Null and Boolean types.

    var sanitized = Application.Properties.getValue("foo") as Null == true;

    Same argument here:

    var sanitized = Application.Properties.getValue("foo") as Number == true;
    TL;DR we should be allowed to compare two values if their types overlap. If their types do not overlap, then I agree it should be a compilation error.
    e.g. Boolean and Boolean or Null are overlapping types
    Boolean and Object or Null are overlapping types (bc Boolean inherits Object)
    Boolean and Null are not overlapping types
  • To be clear, I think this is a bug because Lang.Boolean inherits Lang.Object, so clearly Null or Object should be comparable to Boolean (as a Boolean is an Object).

    It's not really that the type checker is disallowing comparisons between *unknown* types (Object is a "known type", it's not "Any" or "Void"), it's the type checker won't allow you to compare a value of type X to another value of poly-type X or Y or ...

    This can be demonstrated by verifying that the following code compiles without error:

    var sanitized = Application.Properties.getValue("foo") as Object == true;

    But this code does not compile

    var sanitized = Application.Properties.getValue("foo") as Object or Null == true;

  • "I would say the new type checking is correct.  getValue() can return a null, and null==true isn't good syntax."

    I have to disagree with this.

    The code isn't literally null == true, it's x == true, where x *may* be null. (In other words, x's type is nullable)

    "var sanitzed =(temp!=null && temp==true) ? true : false;"

    The point was already made that this is a waste of code.

    Counter-examples:

    1) C#

    int? x = 5; // x is nullable
    if (x == null) { //this compiles fine
    // do stuff
    }

    2) Typescript

    - typescript lets you compare any type to null (or undefined), even if that type is not nullable (to be fair, this is not ideal)

    - however, there is a linting rule to catch comparisons of non-nullable types to null, but it still lets you compare a *nullable* type to null

    https://palantir.github.io/tslint/rules/strict-type-predicates/

    https://github.com/microsoft/TypeScript/issues/17896

    3) By the same logic, if x is a number or boolean, then x == 5 should be forbidden, since false == 5 "isn't good syntax". As a matter of fact, Monkey C also disallows this, but I find that to be absurd.

    I'd like to see an example of similar design in a popular language.

  • I would say the new type checking is correct.  getValue() can return a null, and null==true isn't good syntax.

    var temp==Application,Properties.getValue("foo");

    var sanitzed =(temp!=null && temp==true) ? true : false;