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

Support non-nullable types. #22

Closed
DartBot opened this issue Oct 10, 2011 · 42 comments
Closed

Support non-nullable types. #22

DartBot opened this issue Oct 10, 2011 · 42 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). core-a type-enhancement A request for a change that isn't a bug

Comments

@DartBot
Copy link

DartBot commented Oct 10, 2011

Admin comment: This is activaly being worked on, see dart-lang/language#110 and https://dart.dev/null-safety

This issue was originally filed by [email protected]


Short version: Null pointers are a really good way to mess up a program at runtime, and I'd like the Dart team to reevaluate whether they're absolutely required.

Slightly longer version: I would say the #­1 cause of issues in my programs (excluding logical errors/requirements errors) are NPEs. Having a language support NPE removal, be it via some clever compiler warning or simply removing null altogether, would be wonderful. I'm personally partial to Scala's method of null removal, but I'm sure PL gurus like yourselves have seen many others.

Dart has a stated goal of avoiding the creation of programs that "are difficult to debug or maintain." NPEs are a huge pain point in this regard. I'd be really happy if the Dart team reevaluated whether they are absolutely required to achieve the other aims.

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


Issue #24 has been merged into this issue.

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


some input from the author of null references:

http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


I have to agree. From reading through the tutorial, it sounds like Dart is handling null even worse than most languages. (Even numbers might be null?!)

Given that types are optional, it would make sense to allow null when the type is unspecified (var or dynamic return), but if the user (programmer) is explicitly saying that this variable is an int, why should the type checker assume that null is ok?

Null can be useful, but if I am choosing to explicitly declare a type, please let me also explicitly say whether it is allowed to be null. I personally would prefer option types, but given that this language seems to be trying to be similar to C++/Java/Python, perhaps declaring things nullable would be better. For example, if you write "nullable int n = 42", then "n = null" would be allowed.

Another option would be to explicitly annotate variables as not null, like "not_null int n = 42", but IMHO, the only advantage to that would be that it wouldn't break existing code, and given that this is a new language, that's not a very good reason.

http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake
http://se.ethz.ch/~meyer/publications/hoare/void-safety.pdf
https://secure.wikimedia.org/wikipedia/en/wiki/Nullable

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


I don't think it is possible to completely forbid null-references in a language that allows dynamic types.

However, on page 73 of the Dart language specification draft, the question is asked "Should we do something with respect to non-nullable types?" and my answer is YES, PLEASE, I'M BEGGING YOU.

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


There was discussion of allowing int? foo to mean that foo might be nullable, and just int foo would mean it wasn't. However, there were some complications, particularly with the idea of types being optional -- ie, removing a non-nullable type annotation could change the behavior of the program.

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


May I ask what these complications look like?
When would removing a non-nullable type change the behavior of the program, and how exactly would the behavior be different?

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


I would suggest to borrow C# concept which separate value type from object and only object can be null.

If we want value type (like int) be null we can use a constructor and wrap it as a object nullable<int>.

A plain int can be null is usually a disaster for dev..

@DartBot
Copy link
Author

DartBot commented Oct 10, 2011

This comment was originally written by [email protected]


Nullable types should definitely be added. At least in the expected form where checker would warn you while prod env would let you assign nulls anyway.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


One example of changing behavior:

int foo = null; // throws NPE
int? foo = null // ok

If you strip off the types, you change the behavior of the program.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


Nullable types need to exist as long as default constraints are identified for the type. Much like C# default(t) with a user pragma like declaration . This would allow for ease of intrinsic types and nullable types based on conditions.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


@JaT I don't understand.
Why would a non-nullable
int foo = null;
throw an exception? I thought the type checker only produces compile-time warnings.

A type warning would be perfectly adequate in this case, and not change the behavior of the program if the type is removed. Or am I missing something?

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


So what happens when you dereference a null pointer (that was declared to be non-nullable) at runtime? I don't see how you usefully continue -- the program is broken, and you are better off failing at the point you assign the null pointer to it.

You can do things like require such variables to be initialized and forbid assignment of a null literal, but in general you are not going to be able to tell if the execution of the program would lead to assigning a null to a variable -- it may even reference external things, such as a server or the DOM, that the compiler can't know the behavior of. Even if you require that such functions be declared as "Foo?" and require explicit null checks when assigning to a "Foo", the server might still return a null when it was declared not to.

@dgrove
Copy link
Contributor

dgrove commented Oct 11, 2011

Removed Type-Defect label.
Added Type-Enhancement, Area-Language labels.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


Added Triaged label.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


jat: I thought I read that when running in production mode any type errors were supposed to be ignored, so types don't affect the behavior of the program. When running with checking enabled they do, but that is regardless of whether you allow non-nullable types.

For example:
int foo = 'World'; // throws something? (not in production at least)
var foo = 'World';

The same applies to your argument wrt a server or the DOM. If it can return a null when it was declared not to, couldn't it just as easily return a string when it was declared not to?

If you are really worried that making types non-nullable by default would result in question marks all over the place, could you at least support explicitly marking a variable as non-nullable? That would be completely backwards compatible, but having it sooner would mean library writers would be more likely to use it, which would benefit everyone.

I still think explicitly marking nullable ones is better, because that is when you should be thinking about the fact that it might be null, and the presence of a marker tends to be a better reminder for people than it's absence.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


Agreed. For a given type T, a variable declared to have type "T" should not be allowed to be null, whereas a variable declared to have type "T?" should be allowed to be null.

I don't see why changing the declaration is not allowed to change the behavior of the program, though. Changing a function from allowing null to not allowing null is always a backwards-incompatible change, and I think -- in a lot of the use cases -- the function would not have actually supported null, to begin with. The use of the annotation just makes this lack of support more well documented and exposes the error sooner.

@munificent
Copy link
Member

munificent commented Oct 11, 2011

I don't see why changing the declaration is not allowed to change the behavior of the program, though.

Well, that is sort of the fundamental idea behind optional types: they're optional. You're of course free to disagree with that concept, but that probably means no amount of feature changes will make Dart into the kind of language you want.

Changing a function from allowing null to not allowing null is always a backwards-incompatible change, and I think

Changing the body of a function to something that no longer will work if you pass null is definitely a backwards-incompatible change. That's different, though, than changing the type annotation of what the function expects.

The use of the annotation just makes this lack of support more well documented and exposes the error sooner.

Right, that's what's great about type annotations (or annotations in general): they give a nice user-visible distillation of what the API expects. But you can get that benefit without the type annotation enforcing what it communicates. That's the model of optional types: it will try to tell you that it thinks you're doing something wrong, but it won't totally prevent you from doing it. You're presumed to be smarter than the type checker, so in the case of a disagreement, it will submit to your will.

@DartBot
Copy link
Author

DartBot commented Oct 11, 2011

This comment was originally written by [email protected]


Ok, I'm afraid I just don't get it. My understanding of optional types is that you don't need to declare the types, but you can if you want to. However, where does it say that declaring the types has no effects?

If declaration types have no effects, then what the hell is the point in adding them back? Isn't enforcing type-safety (which would include nullability), a motivation for developers to add back types? If types have no purpose other than documentation, what's the point?

@munificent
Copy link
Member

However, where does it say that declaring the types has no effects?

Gilad can explain it better than I can: http://www.dartlang.org/articles/optional-types/

then what the hell is the point in adding them back?

They do a few things for you:

  1. They document the expected types of a function for other programmers looking at your code.
  2. You can run Dart code in "checked" mode. When you do, all type annotations will be dynamically checked and report an error if the types don't match. (By "dynamically" I mean, at runtime, when you assign to a variable with a declared type, it will check then.)
  3. Tools (such as the Dart compiler) may also opt to use those type annotations to perform static type checking and report static type errors that they find.

I think those cover most of what you want from types (performance is the other piece, but I think Dart can be plenty fast without that), but note that none of them affect the runtime behavior in unchecked mode. So you can get what you want from types, but you aren't required to satisfy the type system in order to run your code.

@sethladd
Copy link
Contributor

FYI a relevant DEP is dart-archive/dart_enhancement_proposals#30

copybara-service bot pushed a commit that referenced this issue Jul 21, 2023
Here's a minimal repro that this CL fixes:

`ui.dart`

```dart
library dart.ui;

import 'dart:ffi';

part 'foo.dart';
```

`foo.dart`

```dart
part of dart.ui;

@Native<Void Function()>(symbol: 'foo_func', isLeaf: true)
external void foo_func();
```

When compiling with `compile_platform.dart` with `--target=dart2wasm`, the following error appears:


```
Unhandled exception:
Verification error: Target=wasm, VerificationStage.afterModularTransformations: Invalid location with target 'wasm' on FunctionNode() (FunctionNode): RangeError (offset): Invalid value: Not in inclusive range 0..56: 91
Context: 'foo_func_$import'.
Node: 'FunctionNode()'.
#0      VerificationErrorListener.reportError (package:kernel/verifier.dart:81:5)
#1      VerifyingVisitor.problem (package:kernel/verifier.dart:222:14)
#2      VerifyingVisitor._getLocation (package:kernel/verifier.dart:1361:7)
#3      VerifyingVisitor._hasLocation (package:kernel/verifier.dart:1370:26)
#4      VerifyingVisitor.getSameLibraryLastSeenTreeNode (package:kernel/verifier.dart:1342:28)
#5      VerifyingVisitor.localContext (package:kernel/verifier.dart:1382:24)
#6      VerifyingVisitor.defaultDartType (package:kernel/verifier.dart:1491:41)
#7      Visitor.visitVoidType (package:kernel/visitor.dart:1309:37)
#8      VoidType.accept (package:kernel/ast.dart:11190:42)
#9      FunctionNode.visitChildren (package:kernel/ast.dart:3919:16)
#10     VerifyingVisitor.visitChildren (package:kernel/verifier.dart:259:10)
#11     VerifyingVisitor.visitWithLocalScope (package:kernel/verifier.dart:266:5)
#12     VerifyingVisitor.visitFunctionNode (package:kernel/verifier.dart:721:5)
#13     FunctionNode.accept (package:kernel/ast.dart:3908:38)
#14     VerifyingVisitor.visitProcedure (package:kernel/verifier.dart:620:19)
#15     Procedure.accept (package:kernel/ast.dart:3311:40)
#16     visitList (package:kernel/ast.dart:14488:14)
#17     Library.visitChildren (package:kernel/ast.dart:591:5)
#18     VerifyingVisitor.visitChildren (package:kernel/verifier.dart:259:10)
#19     VerifyingVisitor.defaultTreeNode (package:kernel/verifier.dart:196:5)
#20     TreeVisitor.visitLibrary (package:kernel/visitor.dart:503:35)
#21     VerifyingVisitor.visitLibrary (package:kernel/verifier.dart:367:11)
#22     Library.accept (package:kernel/ast.dart:577:38)
#23     visitList (package:kernel/ast.dart:14488:14)
#24     Component.visitChildren (package:kernel/ast.dart:14320:5)
#25     VerifyingVisitor.visitChildren (package:kernel/verifier.dart:259:10)
#26     VerifyingVisitor.visitComponent (package:kernel/verifier.dart:342:7)
#27     Component.accept (package:kernel/ast.dart:14313:38)
#28     VerifyingVisitor.check (package:kernel/verifier.dart:171:15)
#29     verifyComponent (package:kernel/verifier.dart:69:20)
...
```

The issue seems to be that after doing this native transformation, the node's `fileUri` references the enclosing library (`ui.dart` above), but the `node.location` references the actual source file (`foo.dart` above) indirectly through `node.fileOffset`.

This ends up being an issue when compiling the platform dill in Google3,   but I didn't look into why `flutter build web --wasm` isn't broken.

Internal bug: b/292172146

Change-Id: I2b8d7d215b2c36354860257ce651d50168e9523d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/315360
Reviewed-by: Ömer Ağacan <[email protected]>
Commit-Queue: Jia Hao Goh <[email protected]>
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). core-a type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests