Thanks to visit codestin.com
Credit goes to github.com

Skip to content

const parameters / type parameters #2776

Open
@mraleph

Description

@mraleph

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<DF>() 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 in dart:ui (see also PluginUtilities.getCallbackHandle and tree-shaking flutter/flutter#118608)
  • They prevent users from hiding restricted APIs inside their code as an implementation detail:
    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.

Proposal

Allow const modifier on parameter and type parameter declarations:

class X {
  const X();
}

class A<const T> {
  void foo<const U>(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.
    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.
    void Function<const T>() bar;
    
    void foo<const T>() {
      bar<int>(); // ok
      bar<T>(); // ok
      bar<List<T>>(); // error
    }
  3. Similar restrictions apply to type literals, e.g. A<int> is a valid instantiation of A<const T> and so is A<T> if T is a const-type-parameter, but A<List<T>> 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureProposed language feature that solves one or more problems

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions