[RFC] Issue 19087 - final switch cannot be used in -betterC#2274
[RFC] Issue 19087 - final switch cannot be used in -betterC#2274PetarKirov merged 1 commit intodlang:masterfrom
Conversation
|
Thanks for your pull request, @wilzbach! Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + druntime#2274" |
|
@WalterBright has already affirmed to me (twice actually) in private e-mail exchange that he is OK replacing Me: I could use @wilzbach: Catching Error is defined to result in undefined behavior. We don't need to cater for such code. So you can use assert? IMO the decision has been made and direction for the future established. This applies equally to #2268 |
src/core/exception.d
Outdated
| extern (C) void onSwitchError( string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow | ||
| { | ||
| throw new SwitchError( file, line, null ); | ||
| assert(0, "No appropriate switch clause found"); |
There was a problem hiding this comment.
One of the things I'd really like to do here, however, is make use of the __FILE__ and __LINE__ parameters in the error message. That was the only reason I hadn't fixed this bug myself. Given that such a trivial thing is actually quite difficult given the limitations discussed in #2268, however, I'd be willing to merge this PR as is, as long as there is a bugzilla issue filed to improve the error message.
There was a problem hiding this comment.
Unfortunately, actually building a message with the file and line number gets to be a bit of a pain when you can't use format from Phobos, and something like sprintf isn't pure (though maybe it should be).
|
They key issue with replacing |
src/core/exception.d
Outdated
| { | ||
| // Consider making this a compile time check. | ||
| throw staticError!SwitchError(file, line, null); | ||
| assert(0, "No appropriate switch clause found"); |
There was a problem hiding this comment.
Another thing we need is the ability to test runtime assertions in unittests. Any ideas?
There was a problem hiding this comment.
It generally works just fine to catch an AssertError in a unit test. That being said, I don't really see anything to test here. The stuff that was in the SwitchError tests all had to do with testing that the members of SwitchError were initialized correctly. I guess that you could do the same with an AssertError, but unless -unittest makes assert(0, ...) still throw an AssertError when the assertion isn't directly in a unittest block when compiling with -release, then you couldn't test it in -release. And honestly, I don't think that testing that the exception is initialized with all of the right values is all that valuable. What you generally need to test is that it's thrown when it's supposed to and not thrown when it's not supposed to, and that would involve testing code that actually triggered __switch_errorT(). All around though, it does get pretty weird to try to test anything that involves assert(0, ...), since pretty much by definition, that code is never supposed to be reached, and it's a serious bug in your program if it is.
There was a problem hiding this comment.
From the spec about unit test blocks and assert expressions:
“Unlike AssertExpressions used elsewhere, the assert is not assumed to hold, and upon assert failure the program is still in a defined state.”
“AssertExpression has different semantics if it is in a unittest or in contract.”
The latter suggests to me that we could change the implementation to throw an exception instead of an error when run inside a unit test block.
There was a problem hiding this comment.
The latter suggests to me that we could change the implementation to throw an exception instead of an error when run inside a unit test block.
Huh? How would that help us? Also final switch wouldn't be nothrow anymore.
There was a problem hiding this comment.
I don't know if it's in the spec but it's been pointed out that Errors should not be caught. Since assert throws an AssertError it's not possible to create a unit test runner that continues after a failed test.
There was a problem hiding this comment.
Haha spec != implementation. It works quite well to catch an AssertError and lots of code in Phobos depends on this, e.g.
ag "collectException.*Error"
std/exception.d
168: assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
176: assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
180: assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
184: assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
311: assert(collectExceptionMsg!AssertError(assertThrown!StringException(
706: auto e = collectException!Error(enforceEx!OutOfMemoryError(false));
714: auto e = collectException!Error(enforceEx!OutOfMemoryError(false, "file", 42));
770: assert(collectException!RangeError(a[4], b));
1843: assert(throwRangeError().ifThrown(0).collectException!RangeError() !is null);
1844: assert(throwRangeError().ifThrown(e=>0).collectException!RangeError() !is null);
ag "catch.*Error"
std/typecons.d
4302: catch (Error e) {}
std/bitmanip.d
708: catch (AssertError ae)
712: catch (AssertError ae)
std/datetime/package.d
293: catch (AssertError e)
320: catch (AssertError e)
std/range/package.d
720: catch (AssertError unused)
std/exception.d
200: catch (AssertError) assert(0);
206: catch (AssertError) assert(0);
212: catch (AssertError) assert(0);
218: catch (AssertError) assert(0);
227: catch (AssertError) thrown = true;
238: catch (AssertError) thrown = true;
249: catch (AssertError) thrown = true;
261: catch (AssertError) thrown = true;
327: catch (AssertError) assert(0);
334: catch (AssertError) assert(0);
341: catch (AssertError) assert(0);
349: catch (AssertError) assert(0);
356: catch (AssertError)
366: catch (AssertError)
376: catch (AssertError)
386: catch (AssertError)
std/experimental/allocator/gc_allocator.d
69: catch (OutOfMemoryError)
std/parallelism.d
4830: catch (ParallelForeachError e) {}
There was a problem hiding this comment.
Haha spec != implementation. It works quite well to catch an AssertError and lots of code in Phobos depends on this, e.g
Yeah, I know. I do that as well. But I would feel better if it was an Exception instead of an Error.
I was thinking a bit more about this and I now changed it too: version (D_Exceptions)
throw new SwitchError( file, line, null );
else
assert(0, "No appropriate switch clause found");So that until we figure out a nice way to do error formatting in |
I'm OK with the change. I'm also OK with leaving it as an |
|
|
Aren't |
No, See item #2 at https://dlang.org/spec/expression.html#assert_expressions |
There's no message printed in release mode: |
|
Specifically, if the condition is known to be |
|
Ah Right! That's actually #11 at https://dlang.org/spec/expression.html#assert_expressions |
|
#2 is for when indicating some code path is unreachable, i.e. int foo(int a)
{
if (a == 3)
return 1;
assert(false);
}In the above example the compiler will not complain that there's no |
| extern (C) void onSwitchError( string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow | ||
| { | ||
| throw new SwitchError( file, line, null ); | ||
| version (D_Exceptions) |
There was a problem hiding this comment.
I suggest adding a comment here that this is temporary, and that once we get either a pure malloc, or an alloca for -betterC that these should be changed to an assert for both cases. Either that, or file a bugzilla issue with the same.
There was a problem hiding this comment.
This discussion doesn't need to hold up resolving issue #19087.
|
Bumping. I don't see a reason this shouldn't be merged. |
PetarKirov
left a comment
There was a problem hiding this comment.
I don't see a reason this shouldn't be merged.
Me too!
That's a discussion that doesn't need to block progress on this issue.
Not fully sure about this as the
SwitchErrordoes contain a lot more information.Maybe we should version this and only use
assertin betterC and otherwise throw the exception?