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

Should dynamic type checks give rise to promotion when performed during declaration pattern matching? #2846

Closed
eernstg opened this issue Feb 14, 2023 · 5 comments
Labels
question Further information is requested

Comments

@eernstg
Copy link
Member

eernstg commented Feb 14, 2023

Consider the following program:

void main() {
  dynamic pi = 3.14;
  final (double v1) = pi;
  int v2 = pi;
}

This program is rejected by the analyzer and the CFE (commit bedcd576560c386e6930573e97ee82596b69fdfe) with the following compile-time errors:

n007.dart:4:12: Error: A value of type 'double' can't be assigned to a variable of type 'int'.
  int v2 = pi;
           ^
Analyzing n007.dart...                 0.7s

  error • n007.dart:4:12 • A value of type 'double' can't be assigned to a
          variable of type 'int'. Try changing the type of the variable, or
          casting the right-hand type to 'int'. • invalid_assignment

This seems to imply that the type of pi has been promoted by being in the continuation of the pattern declaration where v1 is declared. This may seem normal, but it would be a somewhat inconsistent approach to promotion because pi isn't promoted in this very similar program with no patterns:

void main() {
  dynamic pi = 3.14;
  double v1 = pi;
  int v2 = pi;
}

In this case there is no compile-time error (but, of course, the initialization of v2 throws at run time), which is a consequence of the fact that pi still has type dynamic when it is used to initialize v2.

So do we treat the dynamic check associated with a cast from dynamic such that it promotes when it occurs as part of a pattern variable initialization, but not when it is part of a regular local variable initialization? We can certainly do that, but I think it's sufficiently surprising to require a clarification and a decision.

Was it just an oversight, the fact that we don't promote pi when it is subject to a dynamic type check because it has type dynamic? Maybe we should just do it in all cases where such type checks are performed? It would be a breaking change (e.g. dynamic invocations on id that just happened to succeed would now be compile-time errors), but it is hardly very breaking in practice, and it probably "good breakage" because it turns dynamic invocations into statically checked ones.

@dart-lang/language-team, WDYT?

@eernstg eernstg added the question Further information is requested label Feb 14, 2023
@eernstg eernstg changed the title Should dynamic checks introduce types of interest when performed during pattern matching? Should dynamic checks introduce types of interest when performed during declaration pattern matching? Feb 14, 2023
@eernstg eernstg changed the title Should dynamic checks introduce types of interest when performed during declaration pattern matching? Should dynamic checks give rise to promotion when performed during declaration pattern matching? Feb 14, 2023
@eernstg eernstg changed the title Should dynamic checks give rise to promotion when performed during declaration pattern matching? Should dynamic type checks give rise to promotion when performed during declaration pattern matching? Feb 14, 2023
@natebosch
Copy link
Member

it probably "good breakage" because it turns dynamic invocations into statically checked ones.

I'm always in favor of these changes.

@lrhn
Copy link
Member

lrhn commented Feb 15, 2023

EDIT: I take this back. Implicit downcasts and explicit casts should not work the same way. I don't want an implicit downcast to introduce a type of interest. And I want to unify coercions more.

I think we should fix the non-record behavior to match.

We generally promote on a successful as cast, and we should do it on an implicit cast too.

void main() {
  dynamic pi = 3.14;
  final double v1 = pi as double;
  int v2 = pi;
}

This works and promotes pi to double (so does a simple pi as double; statement), and I'd really like that an implicit downcast works precisely the same way as an explicit downcast.

It is a potentially breaking change, because we downcast away from dynamic, which might hide members.

void main() {
  dynamic pi = 3.14;
  final num v1 = pi;
  print(pi.toStringAsFixed(5)); // only available on double, works today.
}

@eernstg
Copy link
Member Author

eernstg commented Mar 17, 2023

Note that #1362 contains a discussion about the same topic in pre-pattern Dart. The support for not promoting was considerably stronger than the support for promoting, but the discussion wasn't closed entirely.

Nevertheless, it seems quite inconsistent that we do get a promotion when the declaration is var (int i) = d; where d is a local variable of type dynamic, but we do not get the promotion with int i = d;.

I think we should ask for an implementation of the non-promoting semantics.

@dart-lang/language-team?

@munificent
Copy link
Member

I feel fairly strongly that the promotion behavior should be the same between non-pattern and pattern declarations.

And I find promoting based on implicit downcasts from dynamic to be pretty non-obvious (and not that useful), so I agree with Erik that I'd like it to not promote in patterns too.

@stereotype441
Copy link
Member

As of dart-lang/sdk@adbf363, var (int i) = d; no longer promotes d, so it is consistent with int i = d;.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants