dart-lang / dart-lang/language
const parameters / type parameters
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
# Background
`dart:ffi` has a number of APIs which require arguments or type arguments to be compile time constants to facilitate static analysis and tree-shaking. For example, `funcPtr.asFunction()` requires `DF` to be a compile time constant, so that compiler could generate appropriate specialised trampoline which handles Dart->C calls. Similar restriction (for the very same reason) applies to the dual API `Pointer.fromFunction(dartFunc)`, where `dartFunc` is expected a compile-time constant (static function tear-off).
Currently these restrictions are enforced using custom `dart:ffi` specific CFE/analyzer passes. These passes have two problems:
- They are `dart:ffi` specific and can't be reused when a similar need arises in some other API. One example of an API which could benefit from this is [`PluginUtilities.getCallbackHandle`](https://api.flutter.dev/flutter/dart-ui/PluginUtilities/getCallbackHandle.html) in `dart:ui` (see also https://github.com/flutter/flutter/issues/118608)
- They prevent users from hiding restricted APIs inside their code as an implementation detail:
```dart
void doSomethingAndCall(void Function() callback) {
c_library.doSomethingAndCall(Pointer.fromFunction(callback)); // error: callback is not a constant
}
void doSomethingAndCall(Pointer<...> callback) { // FFI type leaks into API
c_library.doSomethingAndCall(callback);
}
```
We have previously experimented with building a feature like this in the linter: see [internal `@mustBeConst` proposal](go/dart-must-be-const).
# Proposal
Allow `const` modifier on parameter and type parameter declarations:
```dart
class X {
const X();
}
class A {
void foo(const U u);
void bar(const X x, {const int v});
}
```
Adding `const` to a parameter or type parameter declaration introduces the following restrictions:
1. At a call site any parameter which has `const` on it must have either a constant argument value or an argument which refers to another `const` parameter. Other arguments are invalid.
```dart
void Function(const X x) bar;
void Function(const int y) baz;
void foo(const X x, const int y) {
bar(const X()); // ok
bar(x); // ok
bar(new X()); // error
int v = 10;
baz(1); // ok
baz(y); // ok
baz(v); // error
}
```
2. Similarly, at a call site any type parameter which has `const` on it must have either a constant fully instantiated type argument value or refer to another const type parameter. Other type arguments are invalid.
```dart
void Function() bar;
void foo() {
bar(); // ok
bar(); // ok
bar>(); // error
}
```
3. Similar restrictions apply to type literals, e.g. `A` is a valid instantiation of `A` and so is `A` if `T` is a const-type-parameter, but `A>` is incorrect.
4. Rules for subtyping (and consequenty for valid overrides) respect `const` on parameters and type parameters in an obvious way: given function types `A = R Function(const B)` and `B = R Function(B)` `B` is a subtype of `A`, but not the other way around. Consequently overriding a function might remove requirement for parameter to be constant, but can't add it.
5. `dynamic` calls to function which contains `const` parameters or `const` type parameters will result in an exception.
/cc @lrhn @leafpetersen @eernstg @munificent @dcharkes @mkustermann
Contributor guide
Research direction
Start by reading the proposal and its examples, then compare the existing dart:ffi constant-argument restrictions with the linked dart:ui PluginUtilities.getCallbackHandle API. Done means resolving the declaration, call-site, type-literal, subtyping, override, and dynamic-call rules into an accepted language design.
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
- 25/100