January 23, 2023

On Friday, 20 January 2023 at 20:28:52 UTC, Walter Bright wrote:

>

On 1/20/2023 3:49 AM, Quirin Schroll wrote:

>
  1. process cannot be virtual.

Virtual functions are meant to be overridden, meaning their attributes are inherited with covariant and contravariant rules. This is incompatible with attribute inference.

>
  1. the argument bound to callback cannot have its parameter types inferred.

The version of this I posted can.

TL;DR: You can infer
either parameter types of callbacks
or attributes of callbacks,
but you can never infer both.

Maybe you misunderstood what I was trying to convey.

I’m guessing you mean this version:

void process()(void delegate() userData) {/*impl*/}

Trying 2.099 (dmd-nightly on run.dlang.org), it cannot infer types. Only non-templates can.

Exhaustive Example

I’m using opApply because it’s (as far as I know) the only callback pattern mentioned in the spec and it’s by far the most restricted. If opApply works, it works with simpler cases, too.

struct S1
{
    int opApply     (scope int delegate(string)       callback)
    { return callback(""); }
}
struct S2
{
    int opApply     (scope int delegate(string) @safe callback) @safe
    { return callback(""); }
}
struct S3
{
    int opApply()   (scope int delegate(string)       callback)
    { return callback(""); }
}
struct S4
{
    int opApply(DG :       int delegate(string))(DG   callback)
    { return callback(""); }
}
struct S5
{
    int opApply(DG                       )(DG         callback)
    { return callback(""); }
}

void main() @safe
{
    foreach (x; S1()) { } // Error: `@safe` function `D main` cannot call
                          //        `@system` function `onlineapp.S1.opApply`
    foreach (x; S2()) { } // OK
    foreach (x; S3()) { } // Error: cannot infer type for `foreach` variable `x`
    foreach (x; S4()) { } // Error: cannot infer type for `foreach` variable `x`
    foreach (x; S5()) { } // Error: cannot infer type for `foreach` variable `x`


    foreach (string x; S1()) { } // Error: `@safe` function `D main` cannot call
                                 //`@system` function `onlineapp.S1.opApply`
    foreach (string x; S2()) { } // OK
    foreach (string x; S3()) { } // Error: none of the overloads of template
                                 // `onlineapp.S3.opApply` are callable using
                                 // argument types (**)
    foreach (string x; S4()) { } // Error: none of the overloads of template
                                 // `onlineapp.S4.opApply` are callable using
                                 // argument types (**)
    foreach (string x; S5()) { } // Error: template instance (++) error instantiating
    // (**) = `!()(int delegate(ref string __applyArg0) pure nothrow @nogc @safe)`
    // (++) = `onlineapp.S5.opApply!(int delegate(ref string) pure nothrow @nogc @safe)`

    foreach (string x; &S4().opApply!(int delegate(string) @safe)) { } // OK
    foreach (string x; &S5().opApply!(int delegate(string) @safe)) { } // OK
}

Type-inferred Attempts

Failure on S1 is due to the lack of “@safe relative to callable argument”; S2 proves that by annotating opApply and restricting callback to @safe arguments.

Failure on S3, S4, and S5 are due to lack of inference in the type-inferred cases; S3 failing is unreasonable to some degree because the callback’s type does not depend on template arguments. The pattern in S4 (type parameter used for the only argument is restricted to a delegate type) could be recognized by the compiler, but that would clearly be an enhancement and not a bug. That S5 fails is to be expected. Information about the types (even arity) of delegate would have to be inferred from the body of opApply.

Explicit-type Attempts

(No change by providing a type: Failure on S1 is due to the lack of “@safe relative to callable argument”; S2 proves that by annotating opApply and restricting callback to @safe arguments.)

I’d have expected S3 to fail because the function won’t ever be inferred @safe because the callback is not restricted to @safe.
S4 and S5 should work, but don’t. The compiler attempts to instantiate them with an inappropriate delegate type: int delegate(ref string) @…. This is a bug. For every argument, unless the call-site uses ref on the argument, the compiler must try a delegate type without ref; for n arguments, this can lead to 2 attempts; this is by nature, it cannot be helped, however, if non-ref ones are tried first, it’s likely they succeed on the first attempt. This can be seen when you instantiate opApply explicitly.

January 23, 2023

On Thursday, 19 January 2023 at 01:51:41 UTC, A moo person wrote:

>

This is a semi rant born from frustration. Currently I'm trying to figure out Mir Ion so I can serialize some data, and oh boy is it frustrating.

We use sbin for [de]serialization, which just works (in our case).

https://code.dlang.org/packages/sbin

-- Bastiaan.

January 23, 2023
On 1/18/2023 8:06 PM, A moo person wrote:
> If the argument is that the library should have been designed to infer the annotations, then why even have the annotations in the language at all if the best practice is to infer them.

Great question! I'll start by saying generally speaking, templates should not be annotated and should allow inference to do its thing.

As for why one would need annotations at all:

1. the inference isn't perfect, in particular if the templates are recursive.

2. if everything is inferred, there's no way to tell code is @safe or not. This would be indistinguishable from having no safety checks at all.

3. use an annotation when a guarantee is desired

4. inference isn't done for conventional functions, because as mentioned before that would cause problems with .di files

5. to enforce a desired coding paradigm
January 23, 2023
Help us help you by posting the bug reports to bugzilla!
January 24, 2023
On Tuesday, 24 January 2023 at 07:42:32 UTC, Walter Bright wrote:
> On 1/18/2023 8:06 PM, A moo person wrote:
>> If the argument is that the library should have been designed to infer the annotations, then why even have the annotations in the language at all if the best practice is to infer them.
>
> Great question! I'll start by saying generally speaking, templates should not be annotated and should allow inference to do its thing.
>
> As for why one would need annotations at all:
>
> 1. the inference isn't perfect, in particular if the templates are recursive.
>
> 2. if everything is inferred, there's no way to tell code is @safe or not. This would be indistinguishable from having no safety checks at all.
>
> 3. use an annotation when a guarantee is desired
>
> 4. inference isn't done for conventional functions, because as mentioned before that would cause problems with .di files
>
> 5. to enforce a desired coding paradigm

2: If everything is inferred, maybe the person doing the code doesn't actually care if it is @safe or not. The problem on @safe is how attributes work right now, super shitty to work now with it (const included). Most of D code does not care with @safe. 90% of my code could be inferred as @safe , but no, I need to explicitly say on every function I need to define because when creating a @safe function, I would not be able to call it.

4: It would not cause any problems with di files. Inference can output annotation to .di files, they can be regenerated whenever you wish to distribute.
February 02, 2023

On Tuesday, 24 January 2023 at 07:45:00 UTC, Walter Bright wrote:

>

Help us help you by posting the bug reports to bugzilla!

I filed issues about opApply in the past. An example is this.

Recognizing ref-ness is already filed if I remember correctly; apart from that, technically speaking, those aren’t bugs, but various degrees of potential enhancements.

The empty template parameter case is the easiest. Not supporting just makes the spec a little easier (function templates don’t infer vs. function templates don’t infer unless instantiating the template with empty parentheses compiles). It’s still rather useless. It cannot infer attributes based on the caller-side delegate supplied: The delegate type – and with it its attributes – is fixed.

Recognizing the restriction pattern int opApply(DG : int delegate(string)(DG dg) would save a lot of typing (enhancement filed as issue 23666). The repetition of up to 16 overloads can be automated using a mixin template (I posted one before in this thread) that takes a implementation template and instantiates it with the given delegate type varied with all combinations of attributes. I tried to improve this so that the mixin template needs not be given the delegate type, but finds the template parameter’s constraint type and uses that. This didn’t work for two reasons: 1. For some reason, the string extracted cannot be mixed-in as a type, even if comprised of built-in types only, e.g. int delegate(int). 2. I know that even if one got the first problem solved, the mixed-in string need not designate a valid type because of various ways scoping works; it could still be a good best-effort solution. The compiler quite likely has information about the parameter constraint, the language just lacks __traits to expose it. (Enhancement filed as issue 23665; it’s only long because it’s inherently complicated.)

1 2 3 4 5
Next ›   Last »