-
-
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
Codegen: compile untyped typeof(exp) correctly #5109
Codegen: compile untyped typeof(exp) correctly #5109
Conversation
@@ -741,6 +741,19 @@ module Crystal | |||
node | |||
end | |||
|
|||
def transform(node : TypeOf) | |||
# When the expression inside `typeof` node is untyped, that is | |||
# `type(raise("").foo)` for example, then `node` is untyped also. |
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.
typeof
5a160e3
to
744aabb
Compare
Thanks but this is wrong, |
If |
@RX14 That's correct, but that's not what this PR is doing. |
@asterite Updated. Okay? |
@makenowjust I think in |
@@ -79,6 +79,12 @@ class Crystal::FixMissingTypes < Crystal::Visitor | |||
end | |||
end | |||
|
|||
def visit(node : TypeOf) | |||
node.expressions.each &.accept self | |||
node.type = @program.no_return.metaclass unless node.type? |
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 should just be @program.no_return
. In the spec too.
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.
Why?
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 correct type for typeof(raise("").foo)
is NoReturn:class
because typeof(raise(""))
is typed as NoReturn:class
.
Sorry, but I don't know where did you get the idea that p typeof(raise "") # => NoReturn Similarly, if you have The last commit you had was correct except it had to return |
@asterite But, we get this error: p typeof(raise("")).foo
# Error in foo.cr:1: undefined method 'foo' for NoReturn:Class
#
# p typeof(raise("")).foo
# ^~~
# |
And It's because the foo method is looked up in the class of the receiver. |
require "./spec/spec_helper"
it "assert typeof(1) against int32" do
assert_type(%(typeof(1))) { int32 }
end
it "assert typeof(1) against int32.metaclass" do
assert_type(%(typeof(1))) { int32.metaclass }
end $ ./bin/crystal run spec.cr
F.
Failures:
1) assert typeof(1) against int32
Failure/Error: result.type.should eq(expected_type)
Expected: Int32
got: Int32:Class
# spec/spec_helper.cr:40
Finished in 259.35 milliseconds
2 examples, 1 failures, 0 errors, 0 pending
Failed examples:
crystal spec spec.cr:3 # assert typeof(1) against int32 Hmm..🤔 |
Ah, you are right, it should test against the metaclass. I still think the fix is wrong, |
You know, I'm not sure #5105 is a bug. The expression has no type, because its trying to search a method in a I'd rather not add more lines to the compiler which I don't think are the correct solution to the problem. |
Well, it is a bug because it crashes the compiler but |
I think def transform(node : TypeOf)
node.type = @program.no_return.metaclass unless node.type?
node
end In |
OK, so I was wrong this whole time 😅 Your solution is more or less OK because the cleanup transformer might change some non-typed calls into NoReturn expressions. And when that happens the Basically this: typeof(raise("").foo) gets transformed to this: typeof(untyped_expression!) where The problem is that in your code the So I was thinking of something like this: diff --git a/src/compiler/crystal/semantic/cleanup_transformer.cr b/src/compiler/crystal/semantic/cleanup_transformer.cr
index 40693c55a..fe5cafae2 100644
--- a/src/compiler/crystal/semantic/cleanup_transformer.cr
+++ b/src/compiler/crystal/semantic/cleanup_transformer.cr
@@ -741,6 +741,17 @@ module Crystal
node
end
+ def transform(node : TypeOf)
+ node = super
+
+ unless node.type?
+ node.unbind_from node.dependencies
+ node.bind_to(node.expressions)
+ end
+
+ node
+ end
+
@false_literal : BoolLiteral?
def false_literal That is, when the What do you think? (the specs you wrote are fine) |
@asterite Perhaps you are right, but I'm not sure it works fine when there is untyped expression inside deep of |
An untyped expression has In other cases, CleanUpTransformer never changes the type of something. It just changes from "no type" to We can try this fix and if later we find another broken case we can fix that, but I think that won't happen. |
@asterite Thank you! I fixed this as you suggested. |
Fixed #5105