dart-lang / dart-lang/language

Support static constraints on type casts and type tests

Open
#4,074 4 comments 2 reactions 0 assignees View on GitHub
small-feature type-queries
Dominant language
TeX
Stars
2.9k
Forks
239
Avg merge
2d 18h
Merged PRs (30d)
14

Description

I do not think we have an issue dedicated to this topic, which came up again in https://github.com/dart-lang/sdk/issues/56624. So here we go:

The `as` operator in Dart leaves everything to the developer, as in "I assume you know what you are doing". Similarly, the `is` operator will happily test any object against any type.

This is fine in the general case because it provides great freedom to inform the type checker about typing properties that are beyond the current static analysis—that is, it's good when we truly know what we are doing.

However, we may well have situations where the intended type casts or type tests are more constrained, e.g., an expression of the form `e as T` may well be written under the assumption that `T` is a proper subtype of the static type of `e`, and it's simply a bug if that relationship doesn't hold. For example, with `num n` in scope, we may at some point evaluate an expression like `n as int`, which could be well justified in the given situation, but `n as String` is inevitably a bug.

Similarly, a type test may well be intended to "be a downcast". For example, with `Object o` in scope, a test like `o is num` may promote `o` to `num`, but `o is int?` will not (because promotion will never occur when the target type isn't a subtype of the current type).

Consequently, it could be useful to have separate syntactic forms of type queries, adding constraints like "the target type must be a subtype of the static type of the scrutinee", or ".. a proper subtype ..", or ".. a supertype ..", and perhaps others.

Strawman syntax: `as<` respectively `is<` for "is a proper subtype", and similar operators for other cases. So we'd have this:

```dart
int? iq;

void main() {
Object? o = 42; // Promoted to `Object` by initializing expression.

// The promotion to `Object` was implicit, so we might not be aware of it.
if (o is int?) {...} // OK, same treatment as today, but does not promote `o`.
if (o is< int?) {...} // Compile-time error, `int?` is not a subtype of `Object`.
if (o is< int) {...} // OK, and promotes `o` to `int`.
...
if (iq is< int) { // OK, but `iq` is not local, so it is not promoted.
var iq2 = iq as< String; // Compile-time error, should be a downcast, but isn't.
var iq3 = iq as< int; // OK, is a downcast.
}

void f(X x) {
void g() {
x as<= Y; // OK, downcast, but not necessarily to a different type.
}
}

// We may even wish to use an upcast (`ys` has type `Object` without it).
Iterable xs = [3];
var ys = someCondition ? ['Hi'] : xs as> Iterable;
}
```

This kind of syntax could go along with other mechanisms dealing with run-time type queries, e.g., the syntax `as?` which is proposed in https://github.com/dart-lang/language/issues/399, where `1 as? int` would yield `1` and `1 as? String` would yield null (rather than throwing). We might then have `e as?< T` to make it a proper downcast, and defaulting to null.

Contributor guide

Open the contributing guide

Research direction

Start by reading this proposal alongside dart-lang/sdk issue 56624 and language issue 399. Compare the suggested constrained `as` and `is` forms with the existing Dart language rules, then clarify the accepted syntax and static semantics. Done means the language design is agreed and specified, including how subtype, proper-subtype, supertype, and nullable cast cases behave.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.