Skip to content

Guard for pattern matched struct triggers warning in function body and not in function clause #13109

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

Closed
sashaafm opened this issue Nov 15, 2023 · 1 comment

Comments

@sashaafm
Copy link
Contributor

Elixir and Erlang/OTP versions

Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [dtrace]

Elixir 1.15.7 (compiled with Erlang/OTP 26)

Operating system

macOS Sonoma 14.1

Current behavior

Hello Elixir devs!

I've come across an issue in a large codebase which I've reduced to the following simpler code snippet:

defmodule Guard do
  defstruct a: 1, b: 2

  defguardp is_uri_struct(foo) when is_binary(foo.host) and is_struct(foo, URI)

  defguardp is_guard_struct(foo) when is_struct(foo, Guard)

  defguard is_uri_or_guard(foo) when is_uri_struct(foo) or is_guard_struct(foo)
end

defmodule Test do
  import Guard

  def fun1(%Guard{} = foo) do
    is_uri_or_guard(foo)
  end

  def fun2(foo) do
    is_uri_or_guard(foo)
  end

  def fun3(%Guard{} = foo) when is_uri_or_guard(foo) do
    "Hello"
  end
end

Upon compiling this code we get one single warning for fun1:

$ elixirc guard.ex
warning: undefined field "host" in expression:

    # guard.ex:15
    arg1.host

expected one of the following fields: __struct__, a, b

where "foo" was given the type %Guard{} in:

    # guard.ex:14
    %Guard{} = foo

where "foo" was given the same type as "arg1" in:

    # guard.ex:15
    {arg1} = {foo}

where "arg1" was given the type %Guard{} in:

    # guard.ex:15
    {arg1} = {foo}

Conflict found at
  guard.ex:15: Test.fun1/1

This warning makes sense as the foo variable is already pattern matched to be a Guard, does the usage of the host field in the is_uri_struct guard cannot happen.

Expected behavior

However, I'd expect the fun3 function to also trigger the same warning as the foo parameter is also being pattern matched into a Guard, thus forcefully not having the host field form the URI struct.

I guess the compiler doesn't perform the same check in the function clause, or it does not "keep" in memory that the foo parameter was already matched as a Guard struct and only does so in the function body.

Thank you!

@josevalim
Copy link
Member

Duplicate of #10485. These will be improved once we start integrating set-theoretic types. :) Thank you for the report!

@josevalim josevalim closed this as not planned Won't fix, can't repro, duplicate, stale Nov 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants