-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add 'unreachable!' method to express unreachable code explicitly #4779
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be best to start using unreachable!
in this PR, just in a seperate commit.
spec/std/unreachable_spec.cr
Outdated
|
||
describe "unreachable!" do | ||
it "raises UnreachableError" do | ||
expect_raises(UnreachableError){ unreachable! } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be covered by the below expect_raises.
afd168c
to
b50a12b
Compare
This is an implementation of crystal-lang#4749.
b50a12b
to
a9b6e56
Compare
@RX14 I don't think it is best, however I'll try it... There are many |
@makenowjust Surely you'd add an |
@RX14 Yes. I am working now in this way. |
Done... |
Add `ASTNode#unreachable!` and `Lexer#unreachable!`
2f7fc61
to
ba47338
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would characterize the use of unreachable!
as more restrictive than you did in a few of your substitutions. I think it should mostly be used to declare entire methods or else branches of a type distinction as unreachable.
If an error is raised based on the non-existence or value of specific variables is not unreachable code but a runtime assertion. I didn't mark all of them but wanted to hear some other thoughts.
@@ -186,7 +186,7 @@ module Crystal | |||
# any case, macro code *should* be simple so that it doesn't | |||
# need to be debugged at runtime (because macros work at compile-time.) | |||
unless filename.is_a?(String) | |||
raise "BUG: expected debug filename to be a String, not #{filename.class}" | |||
unreachable! "expected debug filename to be a String, not #{filename.class}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be unreachable!
. It looks more like an assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked this code and all points calling file_and_dir
, then I think here is unreachable surely. file_and_dir
is called by 5 points: L12, L157, L226, L259, L270. L12 passes String
to file_and_dir
directly. L259 passes filename
variable, which is typed String
for now. L157, L226 and L270 pass location.filename
, however this location
comes from location.original_location
. original_location.filename
is always String
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then what is the purpose of this type check anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately location.filename
's type is String | VirtualFile
although location
is original_location
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still, this is not unreachable code - it just checks for an invalid argument.
src/compiler/crystal/codegen/fun.cr
Outdated
unless func | ||
raise "BUG: #{name} is not defined" | ||
end | ||
unreachable! "#{name} is not defined" unless func |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This too looks more like a runtime assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #4779 (comment). I think it is related.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the same thing I am concerned about.
src/compiler/crystal/semantic/ast.cr
Outdated
@@ -13,6 +13,10 @@ module Crystal | |||
::raise exception_type.for_node(self, message, inner) | |||
end | |||
|
|||
def unreachable!(message = "unreachable", inner = nil, exception_type = Crystal::TypeException) | |||
::raise exception_type.for_node(self, "BUG: #{message}", inner) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this raise an UnreachableError
to be compatible with ::unreachable!
? Otherwise it gets confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is difficult to raise UnreachableError
with keeping node information. Perhaps we will feel confused in future in debug if disappear node trace.
Should |
The purpose of |
I want to revert ba47338 for now. I think all @RX14 Please close your "requested changes". |
Well looking closer at the individual instances I mostly agree: Almost all uses are actually not unreachable code. For example if a |
Yes, and union type is also. def string_or_int32(value : String | Int32)
case value
when String then :string
when Int32 then :int32
else unreachable!
end
end
Why not use |
Yeah, union types are another usecase. I mean methods that could from the compilers perspective technically receive a call and must exist for some reason, but should never actually be called. This could be the case when a supertype has a method that makes no sense in the subtype - the subtype must still implement it (and default to parent definition). |
Where is such a case in this repository? or is there such a case in this repository? |
This reverts commit ba47338.
@straight-shoota You thought #4775 (comment)? |
For example. I am not sure if there is something similar in the compiler repo. |
@RX14 🏓 |
There is a Line 139 in 98af739
perhaps it can be replaced with unreachable ?
|
Is this even wanted now, because of #4837? |
src/kernel.cr
Outdated
# When it is called, it raises `UnreachableError` with given *message*. | ||
# However you will never see it if you use this method accurately. | ||
def unreachable!(message = "unreachable") : NoReturn | ||
raise UnreachableError.new("BUG: #{message}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't hardcode "BUG: ..." in every unreachable message. Please move "Bug: " inside the default value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't object if it eventually gets merged, but my vote goes to reject this.
This brings zero improvement over the the actual raise "unreachable"
. It's even encouraging to use an unhelpful generic message, whereas manually raising could push to have an helpful and explanatory message instead.
Closing. This is mostly superseeded by exhaustive case. |
This is an implementation of #4749.
This PR introduces a new error class
UnreachableError
andunreachable!
method raises this error.After merging this, I'll try to fix to useunreachable!
instead ofraise "BUG: ..."
in stdlib and compiler source code.