October 24, 2023
https://issues.dlang.org/show_bug.cgi?id=24197

          Issue ID: 24197
           Summary: Allow typeof(return) in template constraints
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: qs.il.paperinik@gmail.com

This is from the first question of [DConf '23 Day 2 Keynote](https://youtu.be/3iWn4S8JV8g?t=2323): How do I communicate to the user of a function template that the return type is a range?

The compiler has all it needs to check it (per template instance). We just cannot express it with D’s syntax.

I see two solutions:

SOLUTION 1: Make template `if` constraints work with `typeof(return)` even if it’s inferred by deferring the constraint check.

SOLUTION 2: Introduce a new kind of template constraint that’s checked after instantiation, a template `assert`.

I’d guess that Solution 2 is easier to implement and communicates better. I like both solutions, tho.

## Solution 1

I have no idea how templates are compiled and how their constraints are
checked, but I’ll assume the constraints are checked before the body is
instantiated.
Of course if `typeof(return)` targets an inferred return type, it cannot be
checked before instantiating the template.

Because a template constraint is a logical expression tree composed of `&&`s,
`||`s and `!`s and other expressions as leafs, I’d imagine it like this:
1. Leaves that contain `typeof(return)` are pretended to be `true` if the leaf
is under an even number of negations, otherwise `false`. (They’re essentially
skipped.)
2. Then the template is instantiated. If there were `typeof(return)` in step 1,
errors are throttled.
3. If the template has constraints with `typeof(return)` (that were skipped),
now that the return type is known, the constraints are checked again.
(Constraints without `typeof(return)` could be pretended `true` or `false` as
above.) If the constraint evaluates to `true`, there were `typeof(return)` in
step 1, and throttled errors in step 2, then report the errors of step 2.

For the most part, constraints will look like this:
```d
auto someAlgo(R)(R range)
    if (isInputRange!R && isInputRange!(typeof(return)))
{ … }
```

## Solution 2

An alternative would be a new construct next to template constraints. I suggest `assert`; while constraints are checked as one of the first steps of template instantiation, template `assert`s would be checked as one of the last steps (at a point the return type is successfully inferred).

```d
auto someAlgo(R)(R range)
    if (isInputRange!R)
    assert (isInputRange!(typeof(return)))
{ … }
```

The template `assert` would be essentially a `static assert` the same way a template `if` constraint essentially is a `static if`.

Template `if` and template `assert` are the compile-time/static analogue of runtime `in` and `out` function contracts.

--