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

Spellchecker hints and type-directed disambiguation for extensible variants: bis #1706

Merged
merged 1 commit into from
Apr 6, 2020

Conversation

Octachron
Copy link
Member

This PR is an alternative implementation of #1154. Rather than tracking the list of extension constructors associated to a type in Env.t, this implementation splits the list of candidates used in Typescore.NameChoice into a list of spellchecking candidates list and a list of disambiguation candidates. Since the list of spellchecking candidates is only used in a user-facing error path; it can afford to be relatively slow contrarily to the disambiguation candidates list.

This patch keeps the same candidate lists as before for both labels and standard constructors. Contrarily, for extension constructors, the disambiguation candidates are generated with a call to lookup_all_constructors and the computation of the list of all extension constructors associated to a specific type is relegated to the spellchecking path.

@Drup
Copy link
Contributor

Drup commented Apr 8, 2018

At a first glance, I like that version a lot better. The code is much more straightforward and is a simplification over the existing implementation.

I'll take time to review more carefully later.

@gasche
Copy link
Member

gasche commented Oct 16, 2018

@Drup, would you by any chance be able to review it now?

Copy link
Contributor

@Drup Drup left a comment

Choose a reason for hiding this comment

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

I had a look at the new implementation. It's much simpler than the previous version, and I believe it is correct. The test file is very comprehensive, so I'm fairly confident about it. I'll let @Octachron decide what he wants to do for the empty type case, but it's very minor.

if cstrs = [] then (* extension constructor path *)
let env = extend t env in
List.map fst ( Env.lookup_all_constructors (Longident.Lident s) env )
else cstrs
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we have empty sum type now, cstrs = [] doesn't necessarily mean it's an extension constructor. That being said, this simply means doing extra work in a very uncommon cases, and doesn't compromise soundness, so I guess it's fine.

You could make this a bit better with the (unexposed) Env.find_type_full function I suppose.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I will start by extending the comment, I believe that optimization for empty sum types can come later.

@Drup
Copy link
Contributor

Drup commented Oct 28, 2018

Side note: when you rebase, do not forget to rewrite your tests using Armael's new error message syntax.

@lpw25
Copy link
Contributor

lpw25 commented Oct 28, 2018

Sorry for not commenting on this proposal earlier, but IIUC what you are doing I'm not sure that the disambiguation feature is a good idea. It looks like you're planning to allow:

module M = struct
  type Foo.t += Bar
end
let x : Foo.t = Bar

but this will break as soon as M is lifted out into its own compilation unit. The ability to lift modules like that is a fundamental property of the language and I don't think it is one we should break lightly.

Supporting things like:

module M = struct
  type Foo.t += Bar
end
open M
type Other.t += Bar
let x : Foo.t = Bar

on the other hand would be absolutely fine. It shouldn't be too hard to restrict the disambiguation to cases such as these where the constructor is already available at the given path -- these are the cases that do not raise warning 40.

Note that this criticism does not apply to spell-checking hints -- it is fine for them to change when a module is lifted out to its own module.

@Octachron
Copy link
Member Author

Your first example still fails with this PR since the constructor is not in the environment, whereas the second one compiles fine.

The extension of the environment in Typecore.disambiguation_candidates is here to handle cases like

module M = struct
  module N = struct
    type t = ..
    type t += A
  end
end
let x : M.N.t = A

by making sure that, at least, extension constructors defined in M.N are in the environment when looking for constructors of the type M.N.t. This behavior does not change if M is lifted to its own compilation unit.

It is still posible to get a warning 40, with

module M = struct type t = .. type t+=A end
let f (x:M.t) = x = A

but this the same behavior as the one for classical variants. Should those case be disallowed for extension constructors?

@alainfrisch
Copy link
Contributor

by making sure that, at least, extension constructors defined in M.N are in the environment when looking for constructors of the type M.N.t.

This feels ad hoc. Some constructors in the type M.N.t would be "favored", only because they are defined in the same module as their type. If one wants to create such "favored" constructors, this should be explicit in the syntax, e.g. by defining the constructors together with their type (not just in the same module, but with a single definition).

But this should perhaps be discussed independently from a more basic kind of disambiguation, that only restricts the set of candidates, both for qualified references (when different constructors with the same name but different types are defined in a single module) and for unqualified ones (when different constructors are available in the current scope, for instance following an open).

@Octachron
Copy link
Member Author

This part could be clearly split to a separate PR. Nevertheless, I see it as merely using all information provided by the user for the disambiguation, by acknowledging that the extension constructors defined along the type are easier to find.

@lpw25
Copy link
Contributor

lpw25 commented Oct 29, 2018

by making sure that, at least, extension constructors defined in M.N are in the environment when looking for constructors of the type M.N.t

Ok, so that is nowhere near as bad as I thought, but I agree with Alain that this is too ad-hoc for my taste. I'm also suspicious of how it works with type aliases like:

module M = struct
  type t = ..
  type t += A
end

module N = struct
  type t = M.t = ..
  type t += A
end

let x : N.t = A

@Octachron
Copy link
Member Author

Octachron commented Oct 29, 2018

Ok, I have removed this ad-hoc open.
(@lpw25 , your example resolved to N.A)

@Drup
Copy link
Contributor

Drup commented Oct 29, 2018

Ensuring that the constructors in scope of the type are visible for the disambiguation seemed fairly reasonable to me, since it's just more disambiguation and doesn't impact correctness, but I'm fine either ways.

@alainfrisch
Copy link
Contributor

This should address https://caml.inria.fr/mantis/view.php?id=6514, right?

@Octachron
Copy link
Member Author

This PR is not enough in its current state: the error for multiple definitions would need to be removed, the shadowing in include would need to be refined to handle

type t = ..
type s = ..
module M = struct type t += A end
module N = struct type s += A end
module A = struct include M include N end

without hiding M.A or N.A, and the typing of extension rebinding in Typedecl should be made to use the disambiguation mechanism.

@lpw25
Copy link
Contributor

lpw25 commented Mar 26, 2020

What's the status of this PR? I got the impression that people were happy with the final design and the code had been reviewed, but it seems to have got blocked anyway.

@Octachron
Copy link
Member Author

I am planning to rebase it after the refactoring of the disambiguation code in #9182. The rebased version will need to re-expose a function to go through all constructors in scope, so your opinion would be welcome.

@Octachron Octachron force-pushed the extension_disambiguated_bis branch 2 times, most recently from 317d7c7 to 24e98c0 Compare March 27, 2020 10:57
@Octachron
Copy link
Member Author

I have rebased the PR, and removed some of the logic that was not needed anymore. The PR is now simply removing the logic for removing shadowed extension construction from the lookup; and adding an escape hatch for searching constructors with the right type in the whole environment in the misspelling hint.

Changes Show resolved Hide resolved
typing/typecore.ml Outdated Show resolved Hide resolved
@Octachron
Copy link
Member Author

After further discussion with @gasche, I have written an alternative implementation that restrict the special handling for extension constructors to the lookup_from_type function in the Constructor module.

This seemed to be a sensible compromise in term of future compatibility (if we add a notion of extension constructors in scope at a type in the environment) and present complexity.

Copy link
Member

@gasche gasche left a comment

Choose a reason for hiding this comment

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

The new implementation is very simple and clear, thanks. I made some nitpick comments, you should decide whether to take them into account or not.

(This is good to merge when the CI is ready; you should either merge or give me the bit of information on whether you want to work more or not.)

typing/typecore.ml Outdated Show resolved Hide resolved
typing/typecore.ml Outdated Show resolved Hide resolved
@Octachron
Copy link
Member Author

Let me fix these minor points and squash everything.

This commit exposes all extension constructors when looking up for
a construction with a given type in the environment.

This makes constructor disambiguation work for extension constructors.

Going one step further, when the name of an extension constructors
is misspelled, we cannot rely on the type to find all possible
names. However, since we are in an error path, we have some
path at hand.
Thus, this commit alters the "lookup_from_type" function in the
Constructor module to make it scan the whole environment for
extension constructors with the right type.

This commit should not change anything for labels and standard constructors.
@Octachron Octachron force-pushed the extension_disambiguated_bis branch from d0011e6 to 4f9f41e Compare April 3, 2020 14:09
@Octachron Octachron merged commit abda688 into ocaml:trunk Apr 6, 2020
EmileTrotignon pushed a commit to EmileTrotignon/ocaml that referenced this pull request Jan 12, 2024
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.

5 participants