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

[Breaking change request] Make stack traces not be null. #40130

Closed
lrhn opened this issue Jan 14, 2020 · 8 comments
Closed

[Breaking change request] Make stack traces not be null. #40130

lrhn opened this issue Jan 14, 2020 · 8 comments
Assignees
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. breaking-change-request This tracks requests for feedback on breaking changes library-async NNBD Issues related to NNBD Release

Comments

@lrhn
Copy link
Member

lrhn commented Jan 14, 2020

Summary

We will replace a null stack trace supplied by users with a default stack trace.

History: dart-lang/language#747
CL: https://dart-review.googlesource.com/c/sdk/+/128660

What is changing:

A lot of async methods currently allow the user to optionally supply a stack trace. Examples include:

  • Completer.completeError
  • StreamController.addError
  • AsyncError constructor

If the user actually calls these methods with no stack trace, or with an explicit null stack trace, then the system will dutifully propagate null all the way to the user error handler (like Future.catchError or an async method try/catch).

We change this so that if no stack trace is supplied, the system will instead insert a default stack trace.
That default stack trace is computed as either:

  1. If the error object implements Error then read its .stackTrace.
  2. If not, or if that is also null, then use a default stack trace, StackTrace.empty.

The default stack trace is similar to StackTrace.fromString(""). It is a unique object not equal to any other stack trace.

Why is this changing?

Because the current behavior means that in null-sound mode all error handling functions need to take StackTrace? (a nullable type) as argument, even though almost all stack traces are non-null in practice.
That's an unnecessary annoyance when writing error handlers (the experience of migrating the platform libraries had many situations of forgetting the ?).
Providing a nullable value is a tax on users of that value, and in this case there is no benefit from it.

So, we change the behavior in order to be able to have a better and more usable null-sound API.

Example

var c = Completer<int>();
var f = c.future;
var onError(Object error, StackTrace stack) {
  print("$error\n$stack");
}
var f2 = f.catchError(onError);
c.completeError("just the error");

This code would not be valid NNBD code because it lacks the ? after StackTrace. That's a small annoyance, but an unnecessary one.

With this change, the code becomes valid, and the $stack won't print null.

Expected impact

Most likely nothing will break.

If you have code which depends on a stack trace being null, then the code will stop working. That is not expected to happen outside of tests.

If you have code which assumes that a stack trace is not null, which is much more common, your code will now be sound.

There will be cases where the default stack trace will be different from the stack trace currently produced. For example, an await Future.error("no stack trace") will currently synthesize a stack trace at the await expression (which is why we haven't seen try ... catch (error, stack) have a null stack value). It will now get the empty stack trace instead.
Again this will likely not matter outside of tests.

The stack trace package may want to adapt to recognize the default empty stack trace.
It is not necessary, the package already works with a trace that has an empty toString, and it creates a trace with no frames.

@lrhn lrhn added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-async breaking-change-request This tracks requests for feedback on breaking changes labels Jan 14, 2020
@Hixie
Copy link
Contributor

Hixie commented Jan 27, 2020

why StackTrace.empty, instead of StackTrace. current?

@lrhn
Copy link
Member Author

lrhn commented Jan 28, 2020

Because StackTrace.current has a significant performance overhead.

If someone throws an exception (not Error) which is intended to be caught by the caller, then there is no need for allocating an expensive unused stack trace.

If it is an Error, then we do use the stack trace already stored on the Error object, if any.

@franklinyow
Copy link
Contributor

cc @Hixie @matanlurey @dgrove for review and approval.

@matanlurey
Copy link
Contributor

Seems fine

@vsmenon
Copy link
Member

vsmenon commented Feb 12, 2020

lgtm for dart

@Hixie
Copy link
Contributor

Hixie commented Feb 13, 2020

I would prefer if we just make StackTrace.current cheap and used that, but I don't feel strongly about it.

@franklinyow franklinyow assigned lrhn and unassigned franklinyow Feb 13, 2020
@franklinyow
Copy link
Contributor

Approved

@mit-mit mit-mit added the NNBD Issues related to NNBD Release label Feb 19, 2020
dart-bot pushed a commit that referenced this issue Mar 19, 2020
Bug: #40130
Change-Id: I13aba0c2a3fa5b1c3d3995f075ffd38f03aca897
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/139880
Reviewed-by: Bob Nystrom <rnystrom@google.com>
@leafpetersen
Copy link
Member

This has landed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. breaking-change-request This tracks requests for feedback on breaking changes library-async NNBD Issues related to NNBD Release
Projects
None yet
Development

No branches or pull requests

7 participants