diff --git a/spec/compiler/semantic/no_return_spec.cr b/spec/compiler/semantic/no_return_spec.cr index d6deea1e6614..f5159f34f44b 100644 --- a/spec/compiler/semantic/no_return_spec.cr +++ b/spec/compiler/semantic/no_return_spec.cr @@ -327,4 +327,25 @@ describe "Semantic: NoReturn" do typeof(raise("").foo) )) { no_return.metaclass } end + + it "types as NoReturn if followed by one-to-many assignment (#15638)" do + assert_type(<<-CRYSTAL) { bool } + def foo(x) + {'a', ""} + end + + def raise(msg) + while true + end + end + + def bar + x = 1 + return true if x.is_a?(Int32) + a, b = foo(x) + end + + bar + CRYSTAL + end end diff --git a/src/compiler/crystal/semantic/cleanup_transformer.cr b/src/compiler/crystal/semantic/cleanup_transformer.cr index 054c7871bd8e..744ea068d997 100644 --- a/src/compiler/crystal/semantic/cleanup_transformer.cr +++ b/src/compiler/crystal/semantic/cleanup_transformer.cr @@ -379,7 +379,14 @@ module Crystal # `temp_assign` is this whole Assign node and its deduced type is same # as the original RHS's type temp_assign = expanded.as(Expressions).expressions.first - type = temp_assign.type + type = temp_assign.type? + + # if the Assign node's RHS is untyped, this and all following + # assignments are unreachable + unless type + return untyped_expression node + end + target_count = node.targets.size has_strict_multi_assign = @program.has_flag?("strict_multi_assign")