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

Stop more cases of infinite recursion #361

Closed
wants to merge 14 commits into from

Conversation

erszcz
Copy link
Collaborator

@erszcz erszcz commented Oct 20, 2021

This builds on top of #356 and #324 and breaks all cycles summed up in #324 (comment). It also terminates on the original example posted in #322. Together with the fixes from https://github.com/erszcz/Gradualizer/tree/start-app-in-rebar-plugin it also terminates on the example project from #360.

Apparently, this breaks a few existing tests, so I'm leaving it as a draft for now.

Copy link
Owner

@josefs josefs left a comment

Choose a reason for hiding this comment

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

This is great work! Thanks for fixing these loop bugs.

I do have a comment about simplifying the way termination works in normalize.

I'd be willing to accept this as is, even if it invalidates some tests, because is dinitively fixes the problem of looping. The tests that fail are related to refinement and, while we should eventually fix those as well, they don't rank as important to me as the looping bugs. Though there is a clear tradeoff here. @zuiderkwast WDYT?

normalize({type, _, record, [{atom, _, Name}|Fields]}, TEnv) when length(Fields) > 0 ->
NormFields = [type_field_type(FieldName, normalize(Type, TEnv))
|| ?type_field_type(FieldName, Type) <- Fields],
-spec normalize(type(), tenv()) -> type().
Copy link
Owner

Choose a reason for hiding this comment

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

I think a simpler, a cheaper way to fix the problem in normalize would be to keep track of whether we are at the "top level" of a type, where we unfold user-defined types, or whether we are deeper down and don't normalize them. That way we wouldn't need to carry a map of unfoldings. The final result would be the same, I believe.

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 agree, this is really great. I think we can live with the broken test cases as Josef mentioned. I haven't done a full code reviewed.

Comment on lines 15 to 19
%% The following are two variants of the above:
%% - when the pattern matches the type used for A
%% - and when it doesn't
-spec unwrap1(rec1(integer())) -> ok.
unwrap1(_) -> ok.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here, it's expected that ok :: A which passes because we don't solve the constraints on type variables. That's why it fail IIUC. I think we can live with that and move this to a type var constraint solver known problem.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Btw, the names of the test cases "infinite_loop4" and similar are not that great, especially when the're not infinite loops anymore. :-) Better describe them as "recursive record with type variable" or similar.

erszcz and others added 14 commits November 5, 2021 17:17
Prior to this commit, the Env got passed around, but was not really used by normalize().
This meant that even an invalid Env didn't trigger any bugs.
Now that we're using the Env to get the union size limit, it started triggering bugs where the env
was not correctly adjusted to env() from tenv().
Given a recursive type, typechecker:normalize() can recurse forever if
we don't take care to stop the recursion. This patch adds a set to
keep track of the types that `normalize` has unfolded so far and stops
unfolding if asked to normalize a type that has already been unfolded.

Ilya identified the problem with recursives types in josefs#322 and offered
a nice example program to demonstrate the problem.

Fixes josefs#322 although the program is still not accepted as it should be
so I'm adding it to the known problems.
@erszcz
Copy link
Collaborator Author

erszcz commented Nov 7, 2021

This is superseded by #368. I'm not sure if we want to merge them separately or just merge #368.

@erszcz
Copy link
Collaborator Author

erszcz commented Nov 14, 2021

Closing this one. Please see #368.

@erszcz erszcz closed this Nov 14, 2021
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.

3 participants