-
Notifications
You must be signed in to change notification settings - Fork 35
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
Join var binds in add_type_pat_union/3 with least upper bound not greatest lower bound #512
Conversation
I confirm that this solves the list comprehension generators being reported as |
No, I'm not sure. I was probably wrong. I don't remember this anymore. Can you give the simplest example possible where
That's good. I don't understand why we get that error for the code on line 1849. Can you explain? For reference, it's do_type_check_expr(Env, {'fun', P, {function, M, F, A}}) ->
case {get_atom(Env, M), get_atom(Env, F), A} of
{{atom, _, Module}, {atom, _, Function}, {integer, _, Arity}} -> |
No worries 👍
On to the example. The quoted snippet, given we merge with GLB, results in -type af_remote_fun() ::
{'fun', anno(), {'function', module(), function_name(), arity()}}
| {'fun', anno(), {'function',
af_atom() | af_variable(),
af_atom() | af_variable(),
af_integer() | af_variable()}}. This leads nowhere, since If we use LUB, on the other hand, we get |
For the record, a simple example of -module(simple).
-spec f({a, 1} | {a, 2}) -> 1 | 2.
f(U) ->
{a, A} = U,
A. Trace (with this PR):
|
Thank you! I'm convinced. |
For the record, the change to LUB leads to some conflicts. For example: 1> Env = gradualizer:env("-type type() :: gradualizer_type:abstract_type().", []).
{env,#{},#{},#{},
#{module => undefined,records => #{},
types =>
#{{type,0} =>
{[],
{remote_type,0,
[{atom,1,gradualizer_type},{atom,1,abstract_type},[]]}}}},
false,false,true,[],30,none,false}
2> typechecker:subtype(gradualizer:type("{user_type, erl_anno:anno(), atom(), [type()]}"), gradualizer:type("type()"), Env).
{true,{constraints,#{},#{},#{}}}
3> typechecker:subtype(gradualizer:type("{type, erl_anno:anno(), atom(), [type()]}"), gradualizer:type("type()"), Env).
{true,{constraints,#{},#{},#{}}}
4> typechecker:subtype(gradualizer:type("{type | user_type, erl_anno:anno(), atom(), [type()]}"), gradualizer:type("type()"), Env).
false Even though |
I've been thinking about it a bit more and I'm not sure if we should allow ?_assert(subtype(?t( {a1, b} ), ?t( {a1, b} | {a2, b} ))),
?_assert(subtype(?t( {a2, b} ), ?t( {a1, b} | {a2, b} ))), hold, make ?_assert(subtype(?t( {a1 | a2, b} ), ?t( {a1, b} | {a2, b} ))), also hold. It resonates to me with this remark about MLstruct:
We want the TypeScript, not the MLstruct, behaviour in Erlang. We cannot afford the equivalence since Erlang tagged tuples are akin to algebraic data type constructors and are used for pattern matching. @zuiderkwast WDYT? |
eb47aae
to
999679a
Compare
75eec9b shows an interesting problem. 999679a distills it into a test case. It resembles 95de84f, which manifested when registering constraints on type vars passed as call arguments - the solution of registering a union upper bound was a bit of a hack. Having fixed the union merge issue in 1 -module(call_intersection_function_with_union_arg_should_pass).
2
3 -export([f/1]).
4
5 -type t() :: t1 | t2.
6
7 -spec f(t()) -> ok.
8 f(T) ->
9 helper(T).
10
11 -spec helper(t1) -> ok;
12 (t2) -> ok.
13 helper(t1) -> ok;
14 helper(t2) -> ok. It's especially well visible with #491:
|
I don't understand everything in this PR but I'd like to note that aside from what has been mentioned, it also fixes this issue I just came across: -spec f(#{a := [integer()]}) -> empty | non_empty.
f(#{a := []}) -> empty;
f(#{a := _List}) -> non_empty.
-spec g(#{a := b | c}) -> integer().
g(#{a := b}) -> 1;
g(#{a := c}) -> 2. The second clause for both |
Thanks, @xxdavid, that's good feedback 👍 |
82cbacc
to
8c23d6c
Compare
There's some light at the end of the tunnel - |
This fixes at least the following self-check error: ebin/typechecker.beam: The pattern {integer, _, Arity} on line 1849 at column 50 doesn't have the type none() It also leads to a lot more self-check errors popping up. However, upon closer inspection they seem valid, so it seems this just uncovers more consistency issues in our types.
…e_{should ->}_pass.erl
8c23d6c
to
0c6362d
Compare
Down to 302 lines of self-check errors. Only 202 to go 😅 |
Apparently, ! is missing from the definition of binary_op() in OTP's erl_parse, too. However, it almost certainly should be there as the relevant AST nodes are built with ?mkop2() macro as all the other binops.
e7a011d
to
6cd7eb7
Compare
This is similar to @xxdavid's #505, but for var binds. I'm not convinced by the comment in the code that var binds should be merged by an intersection (aka GLB). @zuiderkwast, I think it's your note from 958459e - are you sure of it? This PR fixes at least:
and likely more similar self-check errors.
I also think it might fix #433, which reported list comprehension generators having type
none()
. I remember troubleshooting this previously, but with no good idea for a fix back then - I think this might be the proper fix, but it needs confirmation.The tradeoff, however, is that this generates A LOT of new self-check errors - but they all seem sensible! For example, they're related to us not matching out the
any
atom in places where it's found in various nodes oftype()
. We just assume the last of such node elements to be a list of types, but don't make sure that's the case and the type checker is not convinced with such an unbacked assumption. Example node on which it might happen:{type, anno(), tuple, any | [type()]}
.ToDo: