www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - strange behavior of by-value function arguments in postcondition

reply Andrzej K. <akrzemi1 gmail.com> writes:
Have a look at the following short example:

https://d.godbolt.org/z/1KoofEx5Y


We have a function that takes arguments by value and has a 
postcondition that refers to these arguments:

```D
int select(int lo, int hi)
   in (lo <= hi)
   out (r; lo <= r && r <= hi)
{
   int ans = lo;
   lo = hi; // why not
   return ans;
}
```

I would intuitively assume that the contract of such a function 
is: "I will not modify your objects, and I will select a number 
from the range that your objects indicate." This expectation of 
mine can be reflected by the following assertions:

```D
void main()
{
     int lo = 1;
     int hi = 4;
     int i = select(lo, hi);
     assert(lo == 1);
     assert(hi == 4);
     assert(i >= 1 && i <= 4);
}
```

The fact that function `select()` modifies its copy of the 
argument, should not be of interest to the caller: it is an 
implementation detail of the function. And all the assertions in 
function `main()` are satisfied. However, the fact that 
postcondition observes the copies of user objects makes the 
postcondition fail.

I realize that this effect is consistent with the mechanics of 
the language. But it seems that the mechanics of the language 
render the postconditions somewhat counterintuitive. The 
postcondition should reflect the contract: it should describe 
something that the caller can also see and understand: not the 
state of internal copies of user input.

Is this not a design bug?
Aug 31 2021
next sibling parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:
 Have a look at the following short example:

 https://d.godbolt.org/z/1KoofEx5Y


 We have a function that takes arguments by value and has a 
 postcondition that refers to these arguments:

 ```D
 int select(int lo, int hi)
   in (lo <= hi)
   out (r; lo <= r && r <= hi)
 {
   int ans = lo;
   lo = hi; // why not
   return ans;
 }
 ```

 I would intuitively assume that the contract of such a function 
 is: "I will not modify your objects, and I will select a number 
 from the range that your objects indicate." This expectation of 
 mine can be reflected by the following assertions:

 ```D
 void main()
 {
     int lo = 1;
     int hi = 4;
     int i = select(lo, hi);
     assert(lo == 1);
     assert(hi == 4);
     assert(i >= 1 && i <= 4);
 }
 ```

 The fact that function `select()` modifies its copy of the 
 argument, should not be of interest to the caller: it is an 
 implementation detail of the function. And all the assertions 
 in function `main()` are satisfied. However, the fact that 
 postcondition observes the copies of user objects makes the 
 postcondition fail.

 I realize that this effect is consistent with the mechanics of 
 the language. But it seems that the mechanics of the language 
 render the postconditions somewhat counterintuitive. The 
 postcondition should reflect the contract: it should describe 
 something that the caller can also see and understand: not the 
 state of internal copies of user input.

 Is this not a design bug?
To me it makes perfect sense as the function would probably translated to something like this: int select(int lo, int hi) { // in assert(lo <= hi); // body int ans = lo; lo = hi; // out int r = ans; assert(lo <= r && r <= hi); return r; }
Aug 31 2021
parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 31 August 2021 at 11:30:16 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:
 Have a look at the following short example:

 https://d.godbolt.org/z/1KoofEx5Y


 We have a function that takes arguments by value and has a 
 postcondition that refers to these arguments:

 ```D
 int select(int lo, int hi)
   in (lo <= hi)
   out (r; lo <= r && r <= hi)
 {
   int ans = lo;
   lo = hi; // why not
   return ans;
 }
 ```

 I would intuitively assume that the contract of such a 
 function is: "I will not modify your objects, and I will 
 select a number from the range that your objects indicate." 
 This expectation of mine can be reflected by the following 
 assertions:

 ```D
 void main()
 {
     int lo = 1;
     int hi = 4;
     int i = select(lo, hi);
     assert(lo == 1);
     assert(hi == 4);
     assert(i >= 1 && i <= 4);
 }
 ```

 The fact that function `select()` modifies its copy of the 
 argument, should not be of interest to the caller: it is an 
 implementation detail of the function. And all the assertions 
 in function `main()` are satisfied. However, the fact that 
 postcondition observes the copies of user objects makes the 
 postcondition fail.

 I realize that this effect is consistent with the mechanics of 
 the language. But it seems that the mechanics of the language 
 render the postconditions somewhat counterintuitive. The 
 postcondition should reflect the contract: it should describe 
 something that the caller can also see and understand: not the 
 state of internal copies of user input.

 Is this not a design bug?
To me it makes perfect sense as the function would probably translated to something like this: int select(int lo, int hi) { // in assert(lo <= hi); // body int ans = lo; lo = hi; // out int r = ans; assert(lo <= r && r <= hi); return r; }
Yep, I was right. Looking at the AST this is what the compiler generates for the function: ```d int select(int lo, int hi) in (lo <= hi) out (r; lo <= r && (r <= hi)) { { assert(lo <= hi); } int ans = lo; lo = hi; __result = ans; goto __returnLabel; __returnLabel: { const ref const(int) r = __result; assert(lo <= r && (r <= hi)); } return __result; } ```
Aug 31 2021
parent Andrzej K. <akrzemi1 gmail.com> writes:
On Tuesday, 31 August 2021 at 11:33:10 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 11:30:16 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:
 Have a look at the following short example:

 https://d.godbolt.org/z/1KoofEx5Y


 We have a function that takes arguments by value and has a 
 postcondition that refers to these arguments:

 ```D
 int select(int lo, int hi)
   in (lo <= hi)
   out (r; lo <= r && r <= hi)
 {
   int ans = lo;
   lo = hi; // why not
   return ans;
 }
 ```

 I would intuitively assume that the contract of such a 
 function is: "I will not modify your objects, and I will 
 select a number from the range that your objects indicate." 
 This expectation of mine can be reflected by the following 
 assertions:

 ```D
 void main()
 {
     int lo = 1;
     int hi = 4;
     int i = select(lo, hi);
     assert(lo == 1);
     assert(hi == 4);
     assert(i >= 1 && i <= 4);
 }
 ```

 The fact that function `select()` modifies its copy of the 
 argument, should not be of interest to the caller: it is an 
 implementation detail of the function. And all the assertions 
 in function `main()` are satisfied. However, the fact that 
 postcondition observes the copies of user objects makes the 
 postcondition fail.

 I realize that this effect is consistent with the mechanics 
 of the language. But it seems that the mechanics of the 
 language render the postconditions somewhat counterintuitive. 
 The postcondition should reflect the contract: it should 
 describe something that the caller can also see and 
 understand: not the state of internal copies of user input.

 Is this not a design bug?
To me it makes perfect sense as the function would probably translated to something like this: int select(int lo, int hi) { // in assert(lo <= hi); // body int ans = lo; lo = hi; // out int r = ans; assert(lo <= r && r <= hi); return r; }
Yep, I was right. Looking at the AST this is what the compiler generates for the function: ```d int select(int lo, int hi) in (lo <= hi) out (r; lo <= r && (r <= hi)) { { assert(lo <= hi); } int ans = lo; lo = hi; __result = ans; goto __returnLabel; __returnLabel: { const ref const(int) r = __result; assert(lo <= r && (r <= hi)); } return __result; } ```
Thanks. This seems to imply that postconditions are a way to automatically inject assertions into function body. And then the postconditions behave as one would expect. However, my understanding of postconditions is that they constitute a part of the function's interface that means something to the caller, even in the absence of the function body. The caller can never be interested if the function's current implementation mutates its copies of the arguments or not. And the contract of the function cannot change only because we have mutated the copies of function arguments.
Aug 31 2021
prev sibling next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:

 I would intuitively assume that the contract of such a function 
 is: "I will not modify your objects, and I will select a number 
 from the range that your objects indicate."
For the first half of that assumption to be valid, you would need this function signature: ```d int select(const int lo, const int hi) {} ```
Aug 31 2021
next sibling parent bauss <jj_1337 live.dk> writes:
On Tuesday, 31 August 2021 at 11:40:42 UTC, Mike Parker wrote:
 On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:

 I would intuitively assume that the contract of such a 
 function is: "I will not modify your objects, and I will 
 select a number from the range that your objects indicate."
For the first half of that assumption to be valid, you would need this function signature: ```d int select(const int lo, const int hi) {} ```
And holds true because this statement is then disallowed: ```d lo = hi; // why not ``` Which means "lo" can never be modified or rather never will be modified. At least in this context considering it's a value-type. If it was a reference type then obviously it could still change as external sources can modify it and only immutable would guarantee that it stays the same.
Aug 31 2021
prev sibling parent reply Andrzej K. <akrzemi1 gmail.com> writes:
On Tuesday, 31 August 2021 at 11:40:42 UTC, Mike Parker wrote:
 On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:

 I would intuitively assume that the contract of such a 
 function is: "I will not modify your objects, and I will 
 select a number from the range that your objects indicate."
For the first half of that assumption to be valid, you would need this function signature: ```d int select(const int lo, const int hi) {} ```
I partially agree with this. I agree with the part that if I declare the by-value arguments as `const` then I can safely refer to their names in postconditions. (I use my definition of "safely", as explained in the beginning of this thread.) However, taking arguments by value (`const` or not) *always* means that I do not modify the caller's objects. This is part of the deal: I make a copy in order not to modify the original. I do not have to declare by-value arguments `const` in order to guarantee that I do not modify the original objects used by the caller to invoke my function. I guess, the question here is, who are the postconditions for? Are they for the caller (to guarantee something that the caller understands)? Or are they for the callee (in order to automatically inject assertions into function body)? If it is the latter, then the current semantics are fine.
Aug 31 2021
parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 31 August 2021 at 12:35:54 UTC, Andrzej K. wrote:
 I guess, the question here is, who are the postconditions for? 
 Are they for the caller (to guarantee something that the caller 
 understands)? Or are they for the callee (in order to 
 automatically inject assertions into function body)? If it is 
 the latter, then the current semantics are fine.
The postconditions are for the maintainer to ensure the function actually works as expected. If the asserts don't pass then the function has a bug. Assert statements are never for the user and always for the maintainer.
Aug 31 2021
next sibling parent reply Andrzej K. <akrzemi1 gmail.com> writes:
On Tuesday, 31 August 2021 at 12:39:59 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 12:35:54 UTC, Andrzej K. wrote:
 I guess, the question here is, who are the postconditions for? 
 Are they for the caller (to guarantee something that the 
 caller understands)? Or are they for the callee (in order to 
 automatically inject assertions into function body)? If it is 
 the latter, then the current semantics are fine.
The postconditions are for the maintainer to ensure the function actually works as expected. If the asserts don't pass then the function has a bug. Assert statements are never for the user and always for the maintainer.
Yup. This explanation is compatible with what the compiler does. What puzzles me now is that if it is for implementer only, why should it be the part of the function signature (as opposed to the function body).
Aug 31 2021
parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 31 August 2021 at 12:45:29 UTC, Andrzej K. wrote:
 On Tuesday, 31 August 2021 at 12:39:59 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 12:35:54 UTC, Andrzej K. wrote:
 I guess, the question here is, who are the postconditions 
 for? Are they for the caller (to guarantee something that the 
 caller understands)? Or are they for the callee (in order to 
 automatically inject assertions into function body)? If it is 
 the latter, then the current semantics are fine.
The postconditions are for the maintainer to ensure the function actually works as expected. If the asserts don't pass then the function has a bug. Assert statements are never for the user and always for the maintainer.
Yup. This explanation is compatible with what the compiler does. What puzzles me now is that if it is for implementer only, why should it be the part of the function signature (as opposed to the function body).
I think it's just because it's better to separate asserts from functionality. It makes it much more readable, especially for larger functions etc. It also makes it possible to assert the return value from functions with multiple return statements but in a single place. Otherwise you'd have to do something hacky or use scope statements, which ultimately ends up being extra boilerplate.
Aug 31 2021
parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Tuesday, 31 August 2021 at 12:59:33 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 12:45:29 UTC, Andrzej K. wrote:
 On Tuesday, 31 August 2021 at 12:39:59 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 12:35:54 UTC, Andrzej K. wrote:
 I guess, the question here is, who are the postconditions 
 for? Are they for the caller (to guarantee something that 
 the caller understands)? Or are they for the callee (in 
 order to automatically inject assertions into function 
 body)? If it is the latter, then the current semantics are 
 fine.
The postconditions are for the maintainer to ensure the function actually works as expected. If the asserts don't pass then the function has a bug. Assert statements are never for the user and always for the maintainer.
Yup. This explanation is compatible with what the compiler does. What puzzles me now is that if it is for implementer only, why should it be the part of the function signature (as opposed to the function body).
I think it's just because it's better to separate asserts from functionality. It makes it much more readable, especially for larger functions etc. It also makes it possible to assert the return value from functions with multiple return statements but in a single place.
In theory precondition and post condition code should be injected at the caller site because it is indeed part of the functions signature. The problem with that is that it would be quite inefficient as it would bloat the code significantly as it would copy the test code at each call site. Except for some of the semantic issues like OP's, putting the condition code in the callee is probably the better solution.
Aug 31 2021
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Tuesday, 31 August 2021 at 12:39:59 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 12:35:54 UTC, Andrzej K. wrote:
 I guess, the question here is, who are the postconditions for? 
 Are they for the caller (to guarantee something that the 
 caller understands)? Or are they for the callee (in order to 
 automatically inject assertions into function body)? If it is 
 the latter, then the current semantics are fine.
The postconditions are for the maintainer to ensure the function actually works as expected.
This is not true and is a complete misunderstanding of Design by Contract and what function contracts are for.
 If the asserts don't pass then the function has a bug.

 Assert statements are never for the user and always for the 
 maintainer.
_Assert_ statements are for the maintainer, but pre/post conditions on functions are absolutely for the user. Given that the user fulfills the requirements specified by the function's pre-conditions, then the user can be certain that the guarantees provided by the function's post-conditions will hold. It is only a matter of convenience that function pre/post conditions use assertions.
Aug 31 2021
parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 31 August 2021 at 16:04:17 UTC, Meta wrote:
 On Tuesday, 31 August 2021 at 12:39:59 UTC, bauss wrote:
 On Tuesday, 31 August 2021 at 12:35:54 UTC, Andrzej K. wrote:
 I guess, the question here is, who are the postconditions 
 for? Are they for the caller (to guarantee something that the 
 caller understands)? Or are they for the callee (in order to 
 automatically inject assertions into function body)? If it is 
 the latter, then the current semantics are fine.
The postconditions are for the maintainer to ensure the function actually works as expected.
This is not true and is a complete misunderstanding of Design by Contract and what function contracts are for.
 If the asserts don't pass then the function has a bug.

 Assert statements are never for the user and always for the 
 maintainer.
_Assert_ statements are for the maintainer,
Pre/post conditions _are_ assert statements tho. User validation should be done using exceptions, not asserts.
Aug 31 2021
parent reply Meta <jared771 gmail.com> writes:
On Wednesday, 1 September 2021 at 06:14:27 UTC, bauss wrote:
 Pre/post conditions _are_ assert statements tho. User 
 validation should be done using exceptions, not asserts.
The fact that they are assert statements is an implementation detail. The point of pre/post conditions is to enforce that certain properties of the function hold, and assertions are just how the language happens to implement that.
Sep 02 2021
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On Thursday, 2 September 2021 at 13:33:34 UTC, Meta wrote:
 On Wednesday, 1 September 2021 at 06:14:27 UTC, bauss wrote:
 Pre/post conditions _are_ assert statements tho. User 
 validation should be done using exceptions, not asserts.
The fact that they are assert statements is an implementation detail. The point of pre/post conditions is to enforce that certain properties of the function hold, and assertions are just how the language happens to implement that.
More specifically, the in condition is a contract that concern the caller - in some way the user of the code. You'll not that, because the in condition is a failure of the caller, then what DMD does is wrong - the contract needs to be at the call site. You might that this is just an implementation detail, but it is in fact very relevant for contract on polymorphic functions, which will do the wrong thing with the current implementation. The out condition is a contract that concern the callee. So in a way, it doesn't concern the user, except to the extent that it provides documentation and what to expect from the callee. Because this topic was specifically about out contracts, the claims were not widely off base.
Sep 02 2021
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Friday, 3 September 2021 at 00:23:48 UTC, deadalnix wrote:
 More specifically, the in condition is a contract that concern 
 the caller - in some way the user of the code. You'll not that, 
 because the in condition is a failure of the caller, then what 
 DMD does is wrong - the contract needs to be at the call site. 
 You might that this is just an implementation detail, but it is 
 in fact very relevant for contract on polymorphic functions, 
 which will do the wrong thing with the current implementation.
You have a point, but it's debatable. To elaborate, let's consider: ``` class A { void foo(int i) in (i > 10) { } } class B : A { override void foo(int i) in (i > 0) { } } void main() { A a = new B; a.foo(5); } ``` What do we know about `a.foo`? We know it's a method of `A`, which has an incondition of `i > 10`. So `a.foo(5)` *should* by all rights error out. It doesn't, because through the grace of our `new B` we have an object that has *actually* relaxed the contract. This actually relates to the matter of common vs civil law, and what in the beautiful German language is called the "umgekehrte Tatbestandsirrtum", or "inverted offense presence misconception". (Isn't German beautiful?) To my knowledge, the US doesn't have the same concept, but it's effectively an inverted mistake of law. Essentially, because civil law lawyers are nerds, they apply the following logic: - if one (reasonably) *doesn't* know one is committing a crime, one cannot be guilty - so if you *think* you are committing a crime while what you're doing is actually legal, your action is punishable, because symmetry is beautiful or something. (Disclaimer: not a lawyer.) The same thing applies here. It is legal to call `a.foo(5)` because `a` is secretly a `B` object. But since you don't know this, you are committing a reverse mistake of law: thinking something is illegal, when it actually isn't, and doing it anyway. So in a common law language like D this passes, but in a civil law language, ie. with asserts at the callsite, it would error.
Sep 02 2021
parent deadalnix <deadalnix gmail.com> writes:
On Friday, 3 September 2021 at 05:47:32 UTC, FeepingCreature 
wrote:
 [lawyer analogy]

 The same thing applies here. It is legal to call `a.foo(5)` 
 because `a` is secretly a `B` object. But since you don't know 
 this, you are committing a reverse mistake of law: thinking 
 something is illegal, when it actually isn't, and doing it 
 anyway. So in a common law language like D this passes, but in 
 a civil law language, ie. with asserts at the callsite, it 
 would error.
The lawyer analogy doesn't quite apply. in and out contract are about detecting errors. When the caller calls A.foo, it has no idea i is actually calling B.foo - Liskov substitution principle is at play here. It therefore doesn't know that it can call the function with the value it does. The fact that it does is therefore a logic error in the program. Either this code should use a B, and it shouldn't pass such parameter to the function. The goal of design by contract is to detect errors in the application, so here you go. The fact that a contract fails is not a punishment, like it is in the law example. It is a blessing. It is your application telling you something is not right before you hit production (hopefully).
Sep 03 2021
prev sibling parent reply bauss <jj_1337 live.dk> writes:
On Thursday, 2 September 2021 at 13:33:34 UTC, Meta wrote:
 On Wednesday, 1 September 2021 at 06:14:27 UTC, bauss wrote:
 Pre/post conditions _are_ assert statements tho. User 
 validation should be done using exceptions, not asserts.
The fact that they are assert statements is an implementation detail. The point of pre/post conditions is to enforce that certain properties of the function hold, and assertions are just how the language happens to implement that.
I would argue that they're there to enforce certain properties of the function to hold true when testing, because you should use exceptions otherwise! D's error handling is primarily exceptions and asserts should never be used for error handling because they shouldn't be in release builds and AFAIK release builds remove them. So if you're writing a library and compiling it for release and it takes user-input then the validation will never occur. It's evident in this: Compile this with the -release switch: ```d import std; void a(int x) in (x > 10) { writeln(x); } void main() { a(12); a(5); } ``` You'll see that a(5) will succeed, so the output is: 12 5 However without -release it will be this: 12 core.exception.AssertError onlineapp.d(3): Assertion failure ---------------- ??:? _d_assertp [0x55b9b3fe87fc] ./onlineapp.d:3 void onlineapp.a(int) [0x55b9b3fe6edf] ./onlineapp.d:8 _Dmain [0x55b9b3fe6f03] The solution here is actually to use exceptions if going by idiomatic D: void a(int x) in (x > 10) { if (x < 10) throw new Exception(x.stringof ~ " is below 10."); writeln(x); } Now regardless of whether you're debugging or publishing it will give an error. Debugging output is the same as before, but -release will now output: 12 object.Exception onlineapp.d(3): x is below 10. ---------------- ./onlineapp.d:3 void onlineapp.a(int) [0x5635e7a70df4] ./onlineapp.d:8 _Dmain [0x5635e7a70e1b] Imagine if x came from an external source, you'd NEED to have some runtime validation at release and not assume everything works during tests/debugging. You'll end up with either silent errors that are hard to debug OR you'll end up with tons of crashes with no clear indication.
Sep 02 2021
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Friday, 3 September 2021 at 06:25:44 UTC, bauss wrote:
 On Thursday, 2 September 2021 at 13:33:34 UTC, Meta wrote:
 On Wednesday, 1 September 2021 at 06:14:27 UTC, bauss wrote:
 Pre/post conditions _are_ assert statements tho. User 
 validation should be done using exceptions, not asserts.
The fact that they are assert statements is an implementation detail. The point of pre/post conditions is to enforce that certain properties of the function hold, and assertions are just how the language happens to implement that.
I would argue that they're there to enforce certain properties of the function to hold true when testing, because you should use exceptions otherwise! D's error handling is primarily exceptions and asserts should never be used for error handling because they shouldn't be in release builds and AFAIK release builds remove them. *snip* You'll end up with either silent errors that are hard to debug OR you'll end up with tons of crashes with no clear indication.
Correct: asserts are not for input validation, they're for logic validation. This is why catching Errors is as close as D gets to "undefined behavior." (Compare the scary warnings on https://dlang.org/library/object/error.html )
 The solution here is actually to use exceptions if going by 
 idiomatic D:
 
 void a(int x) in (x > 10) { if (x < 10) throw new 
 Exception(x.stringof ~ " is below 10."); writeln(x); }
Eh, I'd argue this shouldn't have an incondition. In my opinion, the compiler would be justified here in removing the exception, since you just told it, effectively, "x is always > 10". So the `x < 10` branch is "dead code". But also, the `if (x < 10)` (really, `x <= 10`) test here is *why* you'd be justified in writing `in (i > 10)` later.
Sep 02 2021
parent reply bauss <jj_1337 live.dk> writes:
On Friday, 3 September 2021 at 06:50:35 UTC, FeepingCreature 
wrote:
 Eh, I'd argue this shouldn't have an incondition. In my 
 opinion, the compiler would be justified here in removing the 
 exception, since you just told it, effectively, "x is always > 
 10". So the `x < 10` branch is "dead code".

 But also, the `if (x < 10)` (really, `x <= 10`) test here is 
 *why* you'd be justified in writing `in (i > 10)` later.
You haven't told the compiler that x will always be > 10. You have told the compiler that you expect it to always be above 10 but it might not be, say if the value came from user-input or a file somewhere in the call-chain. You have no way to guarantee that something always is something for the compiler, UNLESS the value can never come from a dynamic source. But in that case you might not need to make any guarantees because the value obviously will always be a compile-time constant and you could just calculate it using ctfe.
Sep 02 2021
parent FeepingCreature <feepingcreature gmail.com> writes:
On Friday, 3 September 2021 at 06:53:40 UTC, bauss wrote:
 On Friday, 3 September 2021 at 06:50:35 UTC, FeepingCreature
 But also, the `if (x < 10)` (really, `x <= 10`) test here is 
 *why* you'd be justified in writing `in (i > 10)` later.
You haven't told the compiler that x will always be > 10. You have told the compiler that you expect it to always be above 10 but it might not be, say if the value came from user-input or a file somewhere in the call-chain. You have no way to guarantee that something always is something for the compiler, UNLESS the value can never come from a dynamic source.
You have a way to guarantee that something always is something - and you've already shown how: `if (i <= 10)`. The `if` justifies the `in`. If I expect `i` to be above 10 but it might not be, I write `if` and return an error. If I expect `i` to always be above 10, but want to sanity check or codify the assumption, I write `assert`. I think when you write `assert(i > 10)` in any form, then `i` being `<= 10` past that point should be illegal, ie. value range propagation should be able to rely on asserts having excluded parts of the range.
Sep 03 2021
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Tuesday, 31 August 2021 at 10:43:41 UTC, Andrzej K. wrote:
 Is this not a design bug?
Yes, it is.
Sep 02 2021