Skip to content

Add missing lower_contravariant call (fixes #9856)#9857

Merged
Octachron merged 3 commits intoocaml:4.11from
lpw25:fix-poly-refs-check
Aug 27, 2020
Merged

Add missing lower_contravariant call (fixes #9856)#9857
Octachron merged 3 commits intoocaml:4.11from
lpw25:fix-poly-refs-check

Conversation

@lpw25
Copy link
Copy Markdown
Contributor

@lpw25 lpw25 commented Aug 25, 2020

#1132 accidentally removed a call to lower_contravariant, presumably because of the lower_contravariant call a few lines up which looks equivalent but isn't. This adds it back.

This is a minimal PR for fixing the bug on the 4.11 branch. We should also add a regression test etc. for the patch to trunk.

@gasche
Copy link
Copy Markdown
Member

gasche commented Aug 25, 2020

I don't think we are in a rush to merge overnight, so maybe we could have the regression tests in the PR, and include similar-looking potential-regressions (with polymorphic methods or local type constructors)?

@Octachron Octachron requested a review from garrigue August 25, 2020 09:56
@Octachron
Copy link
Copy Markdown
Member

We need a proper review before integrating the fix in panic mode. This means that there is probably time to write some regression tests in parallel.

@lpw25
Copy link
Copy Markdown
Contributor Author

lpw25 commented Aug 25, 2020

The regression tests are not left out to save time, but to (very slightly) decrease the chance that this patch introduces new problems. If people prefer I can add the test on this branch but in general I think it is better for a bugfix release to include the bugfix and nothing more.

@gasche
Copy link
Copy Markdown
Member

gasche commented Aug 25, 2020

I wise man wrote somewhere that the issue fixed by this PR could have beeen

prevented by people adding tests when they introduce features, adding regression tests when they fix bugs and adding tests that are missing when they make changes to features.

Let's write those tests!

@shindere
Copy link
Copy Markdown
Contributor

shindere commented Aug 25, 2020 via email

Copy link
Copy Markdown
Contributor

@garrigue garrigue 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 that this call to lower_contravariant was done in 4.10 inside check_univars and apparently removed in 4.11, so this seems to be a correct fix.
I also would like to see a regression test.

@lpw25 lpw25 force-pushed the fix-poly-refs-check branch from fc0268c to 203ef1f Compare August 26, 2020 07:57
@lpw25
Copy link
Copy Markdown
Contributor Author

lpw25 commented Aug 26, 2020

I've moved the regression test to this PR and added a Changes entry

^^^^^^^^
Error: This field value has type 'b option ref which is less general than
'a. 'a option ref
|}]
Copy link
Copy Markdown
Member

@gasche gasche Aug 26, 2020

Choose a reason for hiding this comment

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

Currently the regression test checks type a . a option ref, which is unsoundly accepted in trunk, and { foo : 'a . 'a option ref }, which is correctly rejected. I would be in favor of adding two other tests to cover different forms of explicit polymorphism:

let test : 'a . 'a list ref = ref [] 
(* unsound in trunk *)

let test (type a) : a list ref = ref []
(* accepted in trunk, with a weak polymorphic type, which I find surprising *)

(Note: arguably type a . a list ref is a combination of both those features, but I prefer to (also) test the simpler forms. I didn't expect that the (type a) version would type-check, for example.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Locally abstract types are quite orthogonal to explicit polymorphism, and thus these two tests seem quite redundant too me?

Copy link
Copy Markdown
Member

@gasche gasche Aug 26, 2020

Choose a reason for hiding this comment

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

I had the impression that the use of a locally abstract type would guarantee that the definition would be (strongly) polymorphic under the corresponding type variable, so clearly the two features are not orthogonal to me. I have no idea whether it was just me thinking this, or whether this is a wide-spread assumption.

I think that my impression partly comes from the similarity between let t (type a) .. and the System F syntax for abstraction over a type variable. I read this code as if a was a name for a type (treated abstractly in the definition) that can be chosen by each user of t, sort of like a lightweight functor notation. With this interpretation the currently-accepted behavior let foo (type a) : a list ref = [] is wrong.

(Another way to object to this behavior is to say that a weak type variable is scoped outside the declaration that contains it, so unifying it with a locally abstract type could be treated as an escape of that local type.)

I just checked, and the documentation does not say that (type a) will be replaced by a a generalizable type variable, just a type variable, so the documentation is not contradicted by this example. I still find it surprising. (But this is orthogonal to the present bug.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actually, the paragraph Polymorphic syntax is quite explicit about this behavior:

The (type typeconstr-name) syntax construction by itself does not make polymorphic the type variable it introduces, but it can be combined with explicit polymorphic annotations where needed.

@Octachron
Copy link
Copy Markdown
Member

Would it make sense to add a comment on generalize_and_check_univars to indicate that it requires that lower_contravariant has been called on its exp argument if we might be in an expansive context?

Looking at the code, this PR fixes the only call site where this precondition was broken.

@lpw25
Copy link
Copy Markdown
Contributor Author

lpw25 commented Aug 26, 2020

This function is not unique on this front: basically anything called generalize has that property. I'm in favour of more documentation in the type checker, but I don't think these kind of ad-hoc comments really help. This bug didn't come from not knowing that the function needed to be called but from thinking that it already had been.

@Octachron Octachron merged commit 5670723 into ocaml:4.11 Aug 27, 2020
Octachron added a commit that referenced this pull request Sep 3, 2020
Add missing `lower_contravariant` call (fixes #9856)

(cherry picked from commit 5670723)
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.

6 participants