If we set a break point at FuncDeclaration::toObjFile at glue.c and print 'fbody->toChars()', we'll see that the function
void main() {
S s;
assert(s <= 20); // fail!?
}
has been semantic into
int main() {
S s = _D1x1S6__initZ;
assert(cast(int)(s.v <= 20) <= 0);
// ^^
return 0;
}
The extra '<= 0' causes the assertion to fail. Apparently DMD thinks there is an opCmp/opEquals. This also applies for '!=':
assert(s != 14);
which is semantic into
assert(!(s.v != 14));