# Why type promotion in following code not work?

**URL:** https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268
**Category:** Help & Support
**Created:** [January 16, 2023, 5:10am UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268 "2023-01-16T05:10:31Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [January 16, 2023, 5:10am UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/1 "2023-01-16T05:10:31Z")

</div>

```crystal
# 1.cr

require "option_parser"

module Translater
  enum Browser
    Firefox
  end

  browser = Browser::Firefox

  OptionParser.parse do |parser|
    parser.on(
      "-b BROWSER",
      "--browser=BROWSER",
      "Specify browser used for scrap, only support firefox for now, default is firefox.
") do |b|
      browser = Browser.parse?(b)

      if browser.nil?
        STDERR.puts "Supported options: #{Browser.names.map(&.downcase).join ", "}"
        exit 1
      end
    end
  end

  case browser # Translater::Browser | Nil
  in Browser::Firefox
    puts "Firefox"
  end
end

```

```auto
In src/1.cr:25:3

 25 | case browser # Translater::Browser | Nil
      ^
Error: case is not exhaustive.

Missing cases:
 - Nil

```

the `browser` in line 25 case statement is never be nil, right?

Thanks.

* * *

Crystal 1.8.0-dev [c6e3116df] (2023-01-13)

LLVM: 14.0.6  
Default target: x86\_64-pc-linux-gnu

---

<div class="post-metadata">

### Author: ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)
#### Post date: [January 16, 2023, 10:00am UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/2 "2023-01-16T10:00:09Z")

</div>

Maybe your code never execute the part ?  
`do |b|`

Maybe have double check, because if it never execute this block, probably it’s possible you value is Nil without know it.

I never use option\_parser personnaly.

**I think as well about something else** : the error say it’s not exhaustive. Actually the enum you made only enum one type. Maybe it’s the main problem here. Can you try to add an another value to your enum ?

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [January 16, 2023, 10:54am UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/3 "2023-01-16T10:54:34Z")

</div>

> [@zw963](#):
>
> the `browser` in line 25 case statement is never be nil, right?

The compiler is unable to proof that due to complications with the closure context. `browser = Browser.parse?(b)` assigns a nilable value. Even if that nilable value afterwards leads to a different code path that never reaches the `case` statement, it requires the closured variable `browser` to be nilable.  
What you can do to avoid this is not assign the nilable return value from `Browser.parse?(b)` directly to `browser`. Instead, use a non-closured local variable and only assign it to the closured `browser` when the `Nil` type is eliminated.

---

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [January 16, 2023, 11:56am UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/4 "2023-01-16T11:56:29Z")

</div>

> [@straight-shoota](#):
>
> Even if that nilable value afterwards leads to a different code path that never reaches the `case` statement, it requires the closured variable `browser` to be nilable.

Should we consider this as a issue? because in fact, browser can never nilable.

Just as you suggested, the expected fix should be introduce a new non-closure variable.

```crystal
value = Browser.parse?(b)

if value.nil?
  STDERR.puts "Supported options: #{Browser.names.map(&.downcase).join ", "}"
  exit 1
else
  browser = value
end

```

But, i consider this is not necessary theoretically.

Thanks

---

<div class="post-metadata">

### Author: ![zw963](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/zw963/32/1623_2.png) [@zw963](https://forum.crystal-lang.org/u/zw963)
#### Post date: [January 16, 2023, 12:00pm UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/5 "2023-01-16T12:00:31Z")

</div>

> [@Fulgurance](#):
>
> Maybe have double check, because if it never execute this block, probably it’s possible you value is Nil without know it.

If never execute this block, `browser` value is enum `Browser::Firefox`, in fact, this issue not involve with OptionParser, add a new enum value, result is same.

---

<div class="post-metadata">

### Author: ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)
#### Post date: [January 16, 2023, 12:58pm UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/6 "2023-01-16T12:58:18Z")

</div>

> [@zw963](#):
>
> because in fact, browser can never nilable.

No, it’s definitely nilable because you assign a value that’s `Browser | Nil`. And it can actually have the value `nil` when `Browser.parse?` does not find a match. Between the assignment of `browser = Browser.parse?(b)` and `exit 1`, it’s `nil`. Since the assignment happens in a captured block (of `parser.on`), it’s not easy to disprove that at the point `case browser` runs, the proc might be exactly in this spot where `browser` is `nil` in a different fiber.

_Theoretically,_ the compiler could maybe become sophisticated enough to figure this out. But I’m not sure if it’s feasible. Definitely not an easy feature to implement.  
This is certainly not a bug, though.

---

<div class="post-metadata">

### Author: ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)
#### Post date: [January 16, 2023, 1:02pm UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/7 "2023-01-16T13:02:42Z")

</div>

What if the block actually runs inside a spawn and between it gets assigned nil and exiting the block fiber switch happens? Then outside the block it will be nil.

---

<div class="post-metadata">

### Author: ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)
#### Post date: [January 16, 2023, 1:11pm UTC](https://forum.crystal-lang.org/t/why-type-promotion-in-following-code-not-work/5268/8 "2023-01-16T13:11:14Z")

</div>

Ideally Option Parser doesn’t capture the block. Maybe it’s possible.
