Joe Lapp
Joe Lapp

Reputation: 2965

Should Dart be inferencing class member types?

In Dart 2.6.0, when I use is to test the type of a class member variable, the conditional code block does not inference the type.

In the following code, the method show1() produces the error, "The getter 's2' isn't defined for the class 'C1'." Meanwhile, the method show2() compiles just fine.

class C1 {
  C1(this.s1);
  String s1;
}

class C2 extends C1 {
  C2(String s1, this.s2) : super(s1);
  String s2;
}

class InferenceTest {
  InferenceTest(this.c);
  C1 c;

  void show1() {
    print(c.s1);
    if (c is C2) print(c.s2); // error
  }

  void show2(C1 c) {
    print(c.s1);
    if (c is C2) print(c.s2); // no error
  }
}

This seems like a bug, but it also seems like I shouldn't be the first to notice this. I encountered the problem trying to use inferencing on widget.myValue from a widget State.

Have I found a bug, or is there something I'm not understanding?

(I'd find this reasonable in a multi-threaded environment like Java, because the value of c could change between the is test and the use of c. But I understand that Dart largely assumes a single thread.)

Upvotes: 1

Views: 94

Answers (1)

lrn
lrn

Reputation: 71653

Dart does not type-promote instance variables, only local variables.

When you test that a local variable has a specific type, then the program can use the variable as if it had the tested type ... with some caveats because the language needs to be absolutely sure that the value doesn't change between the test and the use. For example, there must be no assignments to the variable in the scope guarded by the test, and there must be no assignments to the variable captured in a closure. Because the variable is local, the compiler can assure itself that this is sufficient to ensure that the variable value doesn't change.

The instance variable c of the class InferenceTest has type C1. There are valid programs where the value of c can change between the test c is C2 and the use c.s2, and it can be changed by code in a completely different library, so the language does not dare to do type promotion in that case. Therefore the type of c is still C1 at the c.s2 access, and you can't access s2.

If you want the promotion, try moving the value into a local variable:

var c = this.c;
if (c is C2) print(c.s2);

This works, which is also what you see in your show2 method because function parameters are local variables and therefore subject to type promotion.

Upvotes: 3

Related Questions