Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect error message when initializer uses setter #39097

Closed
jtlapp opened this issue Oct 24, 2019 · 3 comments
Closed

Incorrect error message when initializer uses setter #39097

jtlapp opened this issue Oct 24, 2019 · 3 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). type-question A question about expected behavior or functionality

Comments

@jtlapp
Copy link

jtlapp commented Oct 24, 2019

When an initializer attempts to use a setter that is present, I get the following error:

'text' isn't a field in the enclosing class.
Try correcting the name to match an existing field, or defining a field named 'text'. dart(initializer_for_non_existent_field)

This error occurs for both text and author in the following:

class Item {
  Item({@required param1, @required param2}) : text = param1, author(param2);

  String _text;
  String _author;

  String get text => _text;
  set text(String value) => _text = value.trim();

  String get author => _author;
  set author(String value) => _author = value.trim();
}

Aside from correcting the error message, I sure would like to know why this isn't allowed. It was explicitly disallowed very early in issue #92. I'm having to add static methods that both the initializers and the setters call. It would be so much easier to just leverage the setter.

  • Dart 2.5.2 macos_x64
@devoncarew
Copy link
Member

@lrhn for the language interpretation

@devoncarew devoncarew added area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). type-question A question about expected behavior or functionality labels Oct 24, 2019
@lrhn
Copy link
Member

lrhn commented Oct 28, 2019

tl;dr: You can't use the instance setter during object initialization because an instance member needs a value for this which does not exist until the object is fully initialized.

The error message is actually correct. You are trying to initialize "text" in the initializer list, you can only initialize fields (instance variables) declared in the same class, so it's reasonable to say that "text is not a field" as error message.

The initializer does not try to access a setter which is there ... because an initializer never accesses a setter, it can only assign an initial value directly to a field (or rather, directly to the underlying storage that will become the field when the object is complete).

The Dart object model enforces a strict no-access policy for uninitialized objects. An object is only accessible after it has been fully initialized, which is why you can never see a final fields before its value is set.

The way an object is initialized is that any final field declared by a class must be initialized either

  • by an initializer expression on the field (final int x = 42;, which is usually a bad idea), or either
  • by an initializing formal (Foo(int this.x)), or
  • by an initializer list entry (Foo(int x) : x = x; or Foo(int x) : this.x = x;), in all generative constructors of the class.

A non-final field can (for now) omit being initialized, in which case it's automatically initialized to null.
Then the super-class constructor is called and it initializes its own fields.

Until all fields have been initialized, nobody can get a reference to the object being created, you cannot use this to refer to the object, which also means that you cannot call instance members like setters which have an implicit this.

A Dart field is really an anonymous storage slot on the object along with three ways to access it:

  • Reading through the implicit getter.
  • Writing through the implicit setter (unless the field is final).
  • Initialization through a constructor invocation (initializing formal, intializer list assignment or initializer expression on the declaration).

Since the setter and getter are instance members, they cannot be used while initializing the object. Until all of the initialization have completed, only field initialization is available, and afterwards, only getters/setters are available.

So, for this example:

  • text = param1 is an invalid initializer list entry because text is not a field declared in the surrounding class. There is an instance setter of that name, but it's not accessible yet. Calling an instance member would mean that it had to have a value for this, and that object is not available yet.
  • author(param2) is invalid because it's not syntactically an initializer list entry (it does not start with name = or this.name =). There is an instance setter named author , but it's not accessible yet, and it can't be called as a function.

@jtlapp
Copy link
Author

jtlapp commented Oct 28, 2019

@irhn, Thank you for the awesome detailed explanation. My error was seeing "field" but reading "member." text and author are members, but not fields. I incorrectly read the message as saying that text and author are not members.

@jtlapp jtlapp closed this as completed Oct 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). type-question A question about expected behavior or functionality
Projects
None yet
Development

No branches or pull requests

3 participants