www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - surprising semantics of the && expression

reply user1234 <user1234 12.de> writes:
So yesterday someone oppened an issue:

https://github.com/dlang/dmd/issues/22500

Which was closed after accurate explanations of Ian. However I'd 
like to know where this semantics are coming from. I suspect C++ 
? What is the history behind ?

Let's mention the POLA. The very basic human-being would expect 
that both the LHS and RHS sub-expressions can be implictly 
convertible to `bool`.
Feb 03
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 2/3/26 18:29, user1234 wrote:
 So yesterday someone oppened an issue:
 
 https://github.com/dlang/dmd/issues/22500
 
 Which was closed after accurate explanations of Ian. However I'd like to 
 know where this semantics are coming from. I suspect C++ ? What is the 
 history behind ?
 ...
C is not very type safe (and used to be even less so), and people get attached to idioms.
 Let's mention the POLA. The very basic human-being would expect that 
 both the LHS and RHS sub-expressions can be implictly convertible to 
 `bool`.
 
 
That is not true anyway. ;p ```d int foo(){ ... } bool a = foo()&&foo(); // ok bool b = foo(); // not ok ``` (It requires explicit convertibility, not implicit.) A common shorthand in D for `cast(bool)e` is `!!e`. `assert(c)` for class reference `c` used to segfault on `null`. I still consistently write `assert(!!c)` today.
Feb 03
parent reply Forum User <forumuser example.com> writes:
On Tuesday, 3 February 2026 at 20:15:42 UTC, Timon Gehr wrote:
 On 2/3/26 18:29, user1234 wrote:
 So yesterday someone oppened an issue:
 
 https://github.com/dlang/dmd/issues/22500
 
 Which was closed after accurate explanations of Ian. However 
 I'd like to know where this semantics are coming from. I 
 suspect C++ ? What is the history behind ?
 ...
C is not very type safe (and used to be even less so), and people get attached to idioms.
Sure, but in C (as in C++) with int b and void foo () b && foo (); is not an idiom. It does not compile.
Feb 03
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
```
b && foo()
```
is there because it is useful, as `if` statements cannot be put inside an 
expression.
Feb 03
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 2/3/26 23:34, Forum User wrote:
 On Tuesday, 3 February 2026 at 20:15:42 UTC, Timon Gehr wrote:
 On 2/3/26 18:29, user1234 wrote:
 So yesterday someone oppened an issue:

 https://github.com/dlang/dmd/issues/22500

 Which was closed after accurate explanations of Ian. However I'd like 
 to know where this semantics are coming from. I suspect C++ ? What is 
 the history behind ?
 ...
C is not very type safe (and used to be even less so), and people get attached to idioms.
Sure, but in C (as in C++) with int b and void foo () b && foo (); is not an idiom. It does not compile.
Why declare the return type as `void` when you could just claim it is `int` instead. Compiles as C as well as D: ```c auto foo(){} void main(){ int b = 1; b && foo(); } ```
Feb 04
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2026 1:20 AM, Timon Gehr wrote:
 Compiles as C as well as D:
 
 ```c
 auto foo(){}
 
 void main(){
      int b = 1;
      b && foo();
 }
 ```
```c int foo() { } ``` compiles without complaint from C. The reason is historical, as K+R C did not require a function to annotate its return type, and defaulted to int. K+R C did not have a void type.
Feb 04
next sibling parent reply Derek Fawcus <dfawcus+dlang employees.org> writes:
 K+R C did not have a void type.
It did, added around Nov '78 (or latest Jul 82). It is just that they never reprinted the book to account for void, enums and struct assignment/pass/return being added. If one grabs some of the historical sources, one can see said support. From DMR's home page: 'However, it turns out that the paper copies of the 7th Edition manual that we printed locally include not only what became Appendix A of K&R 1, but also a page entitled "Recent Changes to C", and I retyped this.' I recall reading this in the papers section at the end of the Unix manuals for the system I had access to. https://www.nokia.com/bell-labs/about/dennis-m-ritchie/cchanges.pdf The page above doesn't specifically mention void, but one can see it in the historical PCC sources if one grabs them (file dates between Jun 81 and Jul 82). Bitsavers has a cooy somewhere, as they were used to build the PC-TCP stack.
Feb 04
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/4/2026 11:41 AM, Derek Fawcus wrote:
 K+R C did not have a void type.
It did, added around Nov '78 (or latest Jul 82).
My copy of K+R was published in 1978. No void!
Feb 04
parent reply Derek Fawcus <dfawcus+dlang employees.org> writes:
On Thursday, 5 February 2026 at 02:04:00 UTC, Walter Bright wrote:
 On 2/4/2026 11:41 AM, Derek Fawcus wrote:
 K+R C did not have a void type.
It did, added around Nov '78 (or latest Jul 82).
My copy of K+R was published in 1978. No void!
So was mine. As I noted, the compiler had it, simply that the book did not mention it. DF
Feb 05
parent Derek Fawcus <dfawcus+dlang employees.org> writes:
On Thursday, 5 February 2026 at 11:34:39 UTC, Derek Fawcus wrote:
 On Thursday, 5 February 2026 at 02:04:00 UTC, Walter Bright 
 wrote:
 On 2/4/2026 11:41 AM, Derek Fawcus wrote:
 K+R C did not have a void type.
It did, added around Nov '78 (or latest Jul 82).
My copy of K+R was published in 1978. No void!
So was mine. As I noted, the compiler had it, simply that the book did not mention it.
More to the point though, for backward compatibility, the compiler still allowed falling off the end of a function returning 'int'. DF
Feb 05
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 2/4/26 19:08, Walter Bright wrote:
 On 2/4/2026 1:20 AM, Timon Gehr wrote:
 Compiles as C as well as D:

 ```c
 auto foo(){}

 void main(){
      int b = 1;
      b && foo();
 }
 ```
```c int foo() { } ``` compiles without complaint from C. ...
If validity as D is not a concern you can also just write: ```c foo(){ } ```
 The reason is historical, as K+R C did not require a function to 
 annotate its return type, and defaulted to int. K+R C did not have a 
 void type.
Yes, and then `b && foo()` works, even if you don't return a value.
Feb 04
prev sibling parent reply Forum User <forumuser example.com> writes:
On Wednesday, 4 February 2026 at 09:20:13 UTC, Timon Gehr wrote:
 Compiles as C as well as D:

 ```c
 auto foo(){}

 void main(){
     int b = 1;
     b && foo();
 }
 ```
$ gcc -Wall -pedantic -pedantic-errors c.c c.c:1:6: error: function definition declared 'auto' 1 | auto foo(){} | ^~~ c.c:1:6: error: return type defaults to 'int' [-Wimplicit-int] c.c:3:6: error: return type of 'main' is not 'int' [-Wmain] 3 | void main(){ | ^~~~ c.c: In function 'main': c.c:5:7: warning: value computed is not used [-Wunused-value] 5 | b && foo(); | ^~ c.c: In function 'foo': c.c:1:12: warning: control reaches end of non-void function [-Wreturn-type] 1 | auto foo(){} | ^
Feb 04
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 2/4/26 22:19, Forum User wrote:
 On Wednesday, 4 February 2026 at 09:20:13 UTC, Timon Gehr wrote:
 Compiles as C as well as D:

 ```c
 auto foo(){}

 void main(){
     int b = 1;
     b && foo();
 }
 ```
$ gcc -Wall -pedantic -pedantic-errors c.c c.c:1:6: error: function definition declared 'auto'     1 | auto foo(){}       |      ^~~ c.c:1:6: error: return type defaults to 'int' [-Wimplicit-int] c.c:3:6: error: return type of 'main' is not 'int' [-Wmain]     3 | void main(){       |      ^~~~ c.c: In function 'main': c.c:5:7: warning: value computed is not used [-Wunused-value]     5 |     b && foo();       |       ^~ c.c: In function 'foo': c.c:1:12: warning: control reaches end of non-void function [-Wreturn-type]     1 | auto foo(){}       |            ^
gcc -w c.c
Feb 04
prev sibling parent reply Iain Buclaw <ibuclaw gdcproject.org> writes:
On Tuesday, 3 February 2026 at 17:29:05 UTC, user1234 wrote:
 So yesterday someone oppened an issue:

 https://github.com/dlang/dmd/issues/22500

 Which was closed after accurate explanations of Ian. However 
 I'd like to know where this semantics are coming from. I 
 suspect C++ ? What is the history behind ?
This has been valid since early D1. https://digitalmars.com/d/1.0/expression.html#OrOrExpression
 Let's mention the POLA. The very basic human-being would expect 
 that both the LHS and RHS sub-expressions can be implictly 
 convertible to `bool`.
Perhaps you've never used a scripting language such as Perl? Ruby and Python are probably the same too.
Feb 03
parent Julian Fondren <julian.fondren gmail.com> writes:
On Tuesday, 3 February 2026 at 23:11:03 UTC, Iain Buclaw wrote:
 On Tuesday, 3 February 2026 at 17:29:05 UTC, user1234 wrote:
 So yesterday someone oppened an issue:

 https://github.com/dlang/dmd/issues/22500

 Which was closed after accurate explanations of Ian. However 
 I'd like to know where this semantics are coming from. I 
 suspect C++ ? What is the history behind ?
This has been valid since early D1.
and only since DIP 1034 in 2021 can || still evaluate to a bool in an assert-like case: ```d import std.stdio : writeln; import core.stdc.stdlib : exit; noreturn fail() { exit(1); } void main(string[] args) { bool b = args.length == 1 || fail(); writeln(b); } ```
 Let's mention the POLA. The very basic human-being would 
 expect that both the LHS and RHS sub-expressions can be 
 implictly convertible to `bool`.
The principle of least astonishment most often comes across as a slightly rude dismissal of the expectations of others. What's the astonishment-math that says that another is wrong for expecting `test || fail()` to work in D? Now, you can do that if you don't take the value of the expression. I imagine that the status quo silences some rare programmer errors, like ```d bool flag; test && complain(); do_something(flag); ``` where during editing you dropped an assignment to `flag`, and didn't notice because the code was still accepted with a different meaning than you intended. The code also would not have worked if you'd kept the assignment, but the two errors cancel out and permit a third error that you are now confused by: how could flag possibly be false when you can see the output from complain()??? The other way that the "principle of least astonishment" comes up is that someone has an unpleasant experience like this and wishes that the language hadn't permitted the confusion. Especially when the feature, that was used without the programmer even knowing about it, doesn't seem to add much. I sympathize a lot with this and have some stories, but you are just going to be confused sometimes, and terms like POLA too easily discount that reality. A new epoch of D with different enough semantics could lead to many errors of confusion just from the difference, even if the new semantics are much cleaner.
Feb 03