Open bugnano opened 3 months ago
Correct. the type system does not do narrowing (or occurrence typing) yet.
Can you provide more details about the code though? In the code snippet that you shared, there is no narrowing. You have simply specified it can be both using or
.
The code where the warning is is like so:
def delete(
%Call{params: %{"conversation_params" => conversation_param_or_id}} = call,
%Session{app_id: app_id} = session
)
when auth_scope(session, :app) do
So there's a Session
struct passed as a parameter to the function, and auth_scope
is designed to work either with Session
or with Conn
.
Given that in the function definition we know that the parameter is a Session
, the guards after is_struct(conn, Conn)
should be ignored by the type checker, because is_struct(conn, Conn)
is known to be false.
Oh, I got it. We need to perform type refinement on the guard and we plan to support this in the next Elixir version. However I am afraid the type system will still warn you have dead code (i.e. part of your guard is always false). You would need to use session_scope
if you only have a session.
I changed the title to reflect that the error is correct but the message is wrong. It should rather say about dead code.
I don't know if it's dead code.
I mean, on other parts of our codebase we use that guard with a Conn
struct, so both parts of the guard are used, just not at the same time.
The type system knows one of the sides of your or
is always false because you have pattern matched on the struct. This is fine:
def delete(
%Call{params: %{"conversation_params" => conversation_param_or_id}} = call,
session
)
when auth_scope(session, :app) do
But this means there is dead code inside the auth_scope guard since is_struct(session, Conn)
is always false:
def delete(
%Call{params: %{"conversation_params" => conversation_param_or_id}} = call,
%Session{app_id: app_id} = session
)
when auth_scope(session, :app) do
Got it, thank you😁
I changed the title to reflect that the error is correct but the message is wrong. It should rather say about dead code.
I'm trying to understand why the "error is correct"? IMO, there is no typing violation, there is no error in the code, dead code generated in a macro should not be an issue, and there should be no warning.
If I understand correctly, the LiveView fix is more of a hack and works because there is no real need to call __live__
, otherwise the hack would fail if a module was passed as a variable instead of a literal module.
Maybe the name should be changed again? I did look for an open bug that mentionned "typing violation" and found none. The tags should also be changed as I believe it is not an enhancement but a bug.
Generally speaking, we only ignore warnings from macros if they are tagged as generated: true. Which is supported for types as of #13727. In the absence of the annotation, it is treated as regular code, which is indeed “dead”.
Got it, thanks.
Elixir and Erlang/OTP versions
Erlang/OTP 27 [erts-15.0] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [jit:ns]
Elixir 1.17.1 (compiled with Erlang/OTP 27)
Operating system
Ubuntu 24.04
Current behavior
In our codebase we have a guard defined like so:
so that we can use the guard
auth_scope
either with a parameter of typeConn
or a parameter of typeSession
, and you can see that we useis_struct
to restrict further guards to access the correct members of the appropriate struct.With Elixir 1.17 the compiler gives the following warning:
because it checks also for the guards of the other data structure.
Expected behavior
No warning being emitted, because
is_struct
narrows down subsequent guards to only 1 data type.