Skip to content
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

Fix for map pattern "doesn't have type any()" warning #389

Merged

Conversation

erszcz
Copy link
Collaborator

@erszcz erszcz commented Feb 23, 2022

Fix for #387.

src/typechecker.erl Outdated Show resolved Hide resolved
src/typechecker.erl Outdated Show resolved Hide resolved
@erszcz erszcz force-pushed the issues/387-map-pattern-doesnt-have-type-any branch from a9c7194 to 679ce43 Compare February 24, 2022 18:51
@erszcz erszcz force-pushed the issues/387-map-pattern-doesnt-have-type-any branch from 679ce43 to 5a8de10 Compare February 24, 2022 19:47
@erszcz erszcz marked this pull request as ready for review February 24, 2022 19:48
@erszcz
Copy link
Collaborator Author

erszcz commented Feb 24, 2022

Ok, I think it's good enough for now - at least it fixes #387. Constraints could be improved according to the FIXME comment, but with no constraint solver it's not testable anyway, so will likely require some attention in the future.

@erszcz
Copy link
Collaborator Author

erszcz commented Feb 24, 2022

@zuiderkwast what do you think?

Copy link
Collaborator

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I didn't take my time to fully understand it, but it looks like how we handle lists and other stuff.

Comment on lines 4319 to 4320
expect_map_type(?type(map, AssocTys), _Env) ->
{assoc_tys, AssocTys, constraints:empty()}.
Copy link
Collaborator

@zuiderkwast zuiderkwast Feb 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a catch-all cause for non-map types here. Before, those types fell through in add_type_pat to throw({type_error, pattern, element(2, Pat), Pat, Ty}) but now add_type_pat catches all map patterns and calls expect_map_type instead.

Maybe we should have a test case for something like this? (IIRC how to reach add_type_pat)

f() ->
    G = g(),
    h(G).

-spec g() -> {tup, le}.
g() -> {tup, le}.

h(#{k := v} = _Map) ->
    ok.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thanks 👍 I don't return {type_error, ...} and all the other expect_*_type functions do.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added https://github.com/erszcz/Gradualizer/blob/f5b2468090636f38b8a1029287b6b862094b0c3c/test/should_fail/map_pattern_fail.erl#L21-L30 but it doesn't seem to trigger anything even withexpect_map_type not returning {type_error, ...}...

The g and h specs are required for the warning to show up:

     22 not_a_map_passed_as_map() ->
     23     G = g(),
  W  24     h(G).     ■ The variable G is expected to have type map() but it has type {tup, le}
     25
     26 -spec g() -> {tup, le}.
     27 g() -> {tup, le}.
     28
     29 -spec h(map()) -> ok.
     30 h(#{k := v} = _Map) ->
     31     ok.

So... it seems to just work 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, the presence of this:

diff --git a/src/typechecker.erl b/src/typechecker.erl
index ebecda1..4d2fb80 100644
--- a/src/typechecker.erl
+++ b/src/typechecker.erl
@@ -4317,7 +4317,9 @@ expect_map_type({var, _, Var}, _Env) ->
     Cs = constraints:add_var(Var, constraints:upper(Var, type(map, any))),
     {assoc_tys, any, Cs};
 expect_map_type(?type(map, AssocTys), _Env) ->
-    {assoc_tys, AssocTys, constraints:empty()}.
+    {assoc_tys, AssocTys, constraints:empty()};
+expect_map_type(Ty, _Env) ->
+    {type_error, Ty}.
 
 %% Rewrite map_field_assoc to map_field_exact to return in pattern types.
 %%

Doesn't seem to make any difference whatsoever (I checked all combinations of specs present/missing).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have coverage on the other expect_map_type clauses, then we must be able to get coverage on the last clause too.

We really should know how to trigger the different parts of the code. We shouldn't rely on guess work and trial-and-error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really should know how to trigger the different parts of the code. We shouldn't rely on guess work and trial-and-error.

Exactly, that's why I'm thoroughly testing it on examples (what could be called "trial-and-error", but I'd argue). The thing is I couldn't find any that would trigger this new clause. If there's no example triggering the code, the code's not needed - that's what I'm trying to say. Anyway, I'll trace execution and see if I can come up with something that would hit the new clause.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep up the good work. 👍 If you can hit the clause just above, then I don't understand how it can be impossible to cover the next clause.....

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course guess work and testing all kinds of examples is useful to figure out how it works. We just shouldn't commit code that we don't know how it works. That's what I meant.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mystery solved:

 expect_map_type(?type(map, AssocTys), _Env) ->
-    {assoc_tys, AssocTys, constraints:empty()}.
+    {assoc_tys, AssocTys, constraints:empty()};
+expect_map_type(Ty, _Env) ->
+    {type_error, Ty}.

protects against a buggy spec as shown by 7aed2b2.

src/typechecker.erl Outdated Show resolved Hide resolved
@erszcz erszcz force-pushed the issues/387-map-pattern-doesnt-have-type-any branch from f5b2468 to 7aed2b2 Compare February 25, 2022 17:12
@erszcz
Copy link
Collaborator Author

erszcz commented Feb 25, 2022

@zuiderkwast Ok, looks good to me, but let me know what you think.

Copy link
Collaborator

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, LGTM!

@erszcz erszcz merged commit 6e89b4e into josefs:master Feb 25, 2022
@erszcz erszcz deleted the issues/387-map-pattern-doesnt-have-type-any branch February 25, 2022 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants