Skip to content

Make the getter of a final instance variable in a constant receiver a constant expression #4455

@eernstg

Description

@eernstg

This is a proposal to add a new kind of constant expressions:

Assume that e is a potentially constant expression and v is an identifier; in this case e.v is potentially constant. Assume that e is a constant expression that evaluates to an instance of a class C that declares a final instance variable v. In this case e.v is a constant expression.

With this definition, evaluation of e.v is guaranteed to be an invocation of the implicitly induced getter named v. This does not involve execution of user-written code, and it can be done at compile time.

This allows constant expressions to use a small amount of the expressive power which is available to non-constant expressions, but has so far been out of reach for constant expressions. For example:

class Person {
  final String firstName, lastName;
  const Person(this.firstName, this.lastName);
}

class Address {
  final String fullName;
  // ...
  const Address.fromNames(String firstName, String lastName): fullName = '$firstName $lastName ...';
  const Address.fromPerson(Person person): fullname = '${person.firstName} ${person.lastName} ...';
}

void main() {
  const person = Person('John', 'Doe');
  const address1 = Address.fromNames(person.firstName, person.lastName);
  const address2 = Address.fromPerson(person);
}

With the current rules we have to use an approach like in address1, because we can't pass multiple constant objects to the Address constructor using a single object which is then explored.

With this feature, we can use the more encapsulated approach of address2. In this case, person is passed to the Address constructor which will then look up its members as needed in order to arrive at the same result.

Another case which is covered as a subset of this feature is the proposal in #2780, which is about enumerated values.

All in all, it seems both benign and useful to allow constant expressions to support this very fundamental kind of abstraction.


Update: Based on @lrhn's comment, I think we should require an explicit marker, such as const, on any getter which is intended to be constant, and on any variable whose implicitly induced getter should be constant. The explicit marker is needed because we would otherwise rely on implementation details of getters in order to accept a given getter as constant, and that would be too easy to break by accident.

Also note the generalization mentioned here which will allow many explicitly written getters to be constant.

As a result of these changes, the following would be supported:

class Person {
  const String _name1, _name2;
  const bool _reversed;
  const Person(String personName, String familyName, {bool reversed = false})
    : _name1 = name1, _name2 = name2, _reversed = reversed;
  const String get firstName => reversed ? _name2 : _name1;
  const String get lastName => reversed ? _name1 : _name2;
  String toString() => "$firstName $lastName";
}

Note that we omit final on variable declarations which are declared to have a constant getter (by having const), because it would be an error for such a variable to be non-final.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhanced-constRequests or proposals about enhanced constant expressionsfeatureProposed language feature that solves one or more problems

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions