www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - why is this cast required?

reply DanielG <simpletangent gmail.com> writes:
```d
alias IntFunc = int delegate();

void exec(IntFunc[] funcs) {}

void main()
{
	int two() => 2;

	exec(
		// why do I have to cast to the exact type that exec() already 
takes?
		cast(IntFunc[])
		[
			() => 1,
			() => two(),
		]);
}
```

Without the cast, the error is:

Error: incompatible types for `(() => 1) : (() => two())`: `int 
function() pure nothrow  nogc  safe` and `int delegate() pure 
nothrow  nogc  safe`
Sep 25
next sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Friday, 25 September 2026 at 11:14:50 UTC, DanielG wrote:
 ```d
 alias IntFunc = int delegate();

 void exec(IntFunc[] funcs) {}

 void main()
 {
 	int two() => 2;

 	exec(
 		// why do I have to cast to the exact type that exec() 
 already takes?
 		cast(IntFunc[])
 		[
 			() => 1,
 			() => two(),
 		]);
 }
 ```

 Without the cast, the error is:

Error: incompatible types for `(() => 1) : (() => two())`: `int 
function() pure nothrow  nogc  safe` and `int delegate() pure 
nothrow  nogc  safe`
Unfortunately you've hit a limitation of how the compiler propagates the expected type into an array literal used as a function argument. You can avoid the cast by explicitly making the first lambda a delegate: ```d exec([ delegate () => 1, () => two(), ]); ``` , or by using an explicitly typed variable, which also provides the necessary context: ```d IntFunc[] funcs = [ () => 1, () => two(), ]; exec(funcs); ``` Why? - Because `() => 1` needs no enclosing context so it is inferred as a function pointer, while `() => two()` calls a non-static nested function and will be treated as a delegate by the compiler.
Sep 25
parent reply DanielG <simpletangent gmail.com> writes:
I see, thank you!

Do you (or anyone else) know the reason for that limitation? Is 
there some obvious situation where the ambiguity is preferable, 
versus simply inferring - or trying to infer - the expected type?

ie, superficially it seems like the compiler could say, "Hey, 
they are trying to use this array in the context of a [type X] 
argument - let's assume that for the moment, and see if it works."

Put another way, why is a function argument treated differently 
than the right side of an explicitly-typed variable assignment?
Sep 25
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Friday, 25 September 2026 at 15:52:04 UTC, DanielG wrote:
 Do you (or anyone else) know the reason for that limitation?
delegates are twice as big as raw function pointers; when doing airplane surgery it would be irresponsible to add an extra dozenial bytes of ram usage as the airplane may fall out of the sky. Theres a `toDelegate` thats suppose to handle it; I think as they added more and more wont-work keywords the situation kept getting worse.
Sep 25
prev sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Friday, 25 September 2026 at 15:52:04 UTC, DanielG wrote:
 I see, thank you!

 Do you (or anyone else) know the reason for that limitation? Is 
 there some obvious situation where the ambiguity is preferable, 
 versus simply inferring - or trying to infer - the expected 
 type?

 ie, superficially it seems like the compiler could say, "Hey, 
 they are trying to use this array in the context of a [type X] 
 argument - let's assume that for the moment, and see if it 
 works."

 Put another way, why is a function argument treated differently 
 than the right side of an explicitly-typed variable assignment?
You could answer your own question yourself by thinking why the ```d exec([ delegate () => 1, () => two(), ]); ``` workaround works? Think, what is the difference between `() => 1` and `delegate () => 1` ? - The `() => 1` lambda does not access any variables or functions in the outer scope. As it is completely self-contained D will optimise it and make it into a raw function pointer (`int function()`). So, you end up with this kind of array literal: `[ int function(), int delegate() ]` . An array like this will be rejected by compiler, so I "fixed" it in my previous post by explicitly making the first element a delegate, and that works.
Sep 26
parent reply DanielG <simpletangent gmail.com> writes:
I appreciate the reply, but that's not the question I am asking.

I am asking why there's a difference between an explicitly typed 
variable, and an explicitly typed function argument - both of 
which 'receive' the mixed-type array literal.

The compiler will implicitly upgrade the function pointers to 
delegates in one case, but not the other. What is the basis, or 
philosophy behind this policy?

Why do variable assignments receive this treatment, but not 
function arguments?

IMO this violates the Principle of Least Astonishment.

```d
alias IntFunc = int delegate();

void main()
{
	int two() => 2;

	// automatically upgrades function pointers to delegates
	// which is exactly what we'd expect ...
	IntFunc[] explicit = [
		() => 1,
		() => two()
	];

	void func(IntFunc[] arg) {
		// but in the case of a function,
		// our VERY EXPLICIT type parameter is seemingly ignored.
		// there is no coercion/upgrading of function pointers to 
delegates
		//   at the call site, as with the variable.
		// BUT WHY?
		// what is the reasoning?
	}
}
```
Sep 26
parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Saturday, 26 September 2026 at 16:49:48 UTC, DanielG wrote:
 I appreciate the reply, but that's not the question I am asking.

 I am asking why there's a difference between an explicitly 
 typed variable, and an explicitly typed function argument - 
 both of which 'receive' the mixed-type array literal.

 The compiler will implicitly upgrade the function pointers to 
 delegates in one case, but not the other. What is the basis, or 
 philosophy behind this policy?

 Why do variable assignments receive this treatment, but not 
 function arguments?

 IMO this violates the Principle of Least Astonishment.
A function argument generally has to be given a type independent of which parameter type it'll end up matching, because overload resolution needs to compare each argument's own type against every candidate's parameter types to pick the best match — if argument inference depended on "the parameter type of whichever overload gets picked," that's circular. A variable declaration ( `IntFunc[] explicit = [...]` ) doesn't have this problem: there's exactly one destination type, known up front, so the compiler can safely bias the literal toward it.
Sep 26
next sibling parent DanielG <simpletangent gmail.com> writes:
Ah, I see now! Thank you, that was exactly the information I was 
looking for.
Sep 26
prev sibling parent Himanshu Sharma <sharmahimanshuarysonint gmail.com> writes:
On Saturday, 26 September 2026 at 19:39:35 UTC, Dejan Lekic wrote:
 On Saturday, 26 September 2026 at 16:49:48 UTC, DanielG wrote:
 [...]
A function argument generally has to be given a type independent of which parameter type it'll end up matching, because overload resolution needs to compare each argument's own type against every candidate's parameter types to pick the best match — if argument inference depended on "the parameter type of whichever overload gets picked," that's circular. A variable declaration ( `IntFunc[] explicit = [...]` ) doesn't have this problem: there's exactly one destination type, known up front, so the compiler can safely bias the literal toward it.
great information
Oct 05
prev sibling parent Tannesover <jimmdox proton.me> writes:
On Friday, 25 September 2026 at 11:14:50 UTC, DanielG wrote:
 ```d
 alias IntFunc = int delegate();

 void exec(IntFunc[] funcs) {}

 void main()
 {
 	int two() => 2;

 	exec(
 		// why do I have to cast to the exact type that exec() 
 already takes?
 		cast(IntFunc[])
 		[
 			() => 1,
 			() => two(),
 		]);
 }
 ```
 https://slopegame-2.io
 Without the cast, the error is:
 
Error: ) incompatible types for `(() => 1) : (() => two())`: 
`int function() pure nothrow  nogc  safe` and `int delegate() 
pure nothrow  nogc  safe`
This is a classic issue with overload resolution in D. To clean up the code without using an explicit cast, you can wrap the array elements with std.algorithm.map!toDelegate or mark the individual function pointer with toDelegate.
Oct 07