-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
PR#6416 et al.: injective mapping between identifiers and printed names #1120
Conversation
Is meaningful location information available when these errors are caught? If so it would be useful to display a summary of where each unique type name comes from after the warning. |
I like the idea of adding location information as a footnote. I will have a look. |
This certainly looks useful. The general design seems fine (in particular the idea of replacing Oide_ident's argument by a reference), but I'm still surprised at the sheer size of the patch. |
The Deferred submodule is not absolutely required, its existence merely stems from my attempt to make it easier to compose error messages without explicitly building all outcome trees before hand. Consequently, it could be replaced by self-enforced discipline and avoiding the printing function defined in fprintf ppf
"@[<hv 2>Values do not match:@ %a@;<1 -2>is not included in@ %a@]"
(value_description id) d1 (value_description id) d2;
show_locs ppf (d1.val_loc, d2.val_loc);; where the outcome trees associated with After the second commit in this PR, the code is altered to let open Deferred in
fprintf ppf
"@[<hv 2>Values do not match:@ %t@;<1 -2>is not included in@ %t@]"
(value_description id d1) (value_description id d2);
show_locs ppf (d1.val_loc, d2.val_loc); in which the outcome trees for The more explicit version would be reset_naming_context ();
let td1 = tree_of_value_description id d1 and td2 = tree_of_value_description id d2 in
fprintf ppf
"@[<hv 2>Values do not match:@ %a@;<1 -2>is not included in@ %a@]"
!Oprint.out_sig_item td1 !Oprint.out_sig_item d2;
show_locs ppf (d1.val_loc, d2.val_loc); |
SInce it was a low hanging fruit, I have added a name conflict explanation footnote to the warning messages, that details the location of the definition of every conflicting identifiers. For instance, type t = A
module M: sig type t val x:t end = struct
type t = B
let x = A
end;; yields now
Similarly, the error message for MPR#6323 example has been extended to
|
85ff82b
to
5357b17
Compare
Since using the internal identifier stamp was making the test suite brittle and was not particularly readable, I have udapted the code to use namespace/name specific counter for unique names, i.e: module O = struct
module type s
type t = A
module M: sig val f: (module s) -> t -> t end =
struct module type s type t = B let f (module X:s) A = B end
end;; emits the following error message
|
Thank you, the namespace/name-specific counters are much easier to read and decipher. |
I'm not sure about this new warning. On the one hand I can see the benefit of warning users that the signature isn't valid -- although it is worth noting that there are other ways that |
It is certainly true that
In this context, I find quite logical to warn users when more manual editing would be needed to Maybe the problem is more the wording of the warning, which should put more emphasis on the "manual intervention needed" interpretation rather than the non-compilable notice. |
Possibly. I'm just a little concerned that people will think they can use It's probably fine, but it does seem worth bearing in mind when considering this change. |
You should consider using gcc's trick to print these locations in an emacs-friendly way:
This is slightly ugly but much easier to navigate once you get used to it. |
b63578e
to
42f673a
Compare
@lpw25 : I agree that is important to not give the impression that this warning can be relied upon by build systems. I have added some words of caution at the end of the warning to make this point more explicit:
@damiendoligez, good point. I have sacrificed some readability to please emacs in f4f5576 |
typing/printtyp.ml
Outdated
|
||
let print_conflicts ?(sep=fun ppf -> Format.fprintf ppf "@;") ppf = | ||
let l = | ||
List.filter (* remove toplevle location, which are not really useful *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"toplevle" -> "toplevel"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks.
8439a59
to
fe9b290
Compare
fe9b290
to
0945af5
Compare
This PR is slowly rotting away in the backyard, and that makes me sad. |
I haven't followed the discussion, but I'm a bit surprised that we'd need new reviewers considering that both Jacques and Leo looked at this. |
IIRC I didn't review the code, I just commented on the idea. |
ocamldoc/odoc_print.ml
Outdated
@@ -14,6 +14,7 @@ | |||
(**************************************************************************) | |||
|
|||
open Format | |||
;; Printtyp.Naming_context.enable false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unusual style in the compiler: the standard style is let () = ...
, which ensures the absence of partial application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks.
I actually, I had only looked at the first version of the PR. |
typing/printtyp.ml
Outdated
|
||
module Namespace = struct | ||
|
||
type t = [`Type|`Module|`Module_type|`Class|`Class_type|`Other] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use a polymorphic variant here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the main reason was because env.ml
is already using polymorphic variant for namespaces (when handling shadowing). I can revert to simple variants.
0945af5
to
a503bfa
Compare
@garrigue , I think it was largely a communication failure on my part. |
a503bfa
to
a2408fc
Compare
a2408fc
to
15fb7e5
Compare
15fb7e5
to
47ea8ed
Compare
47ea8ed
to
3d1cf26
Compare
Ok, I have removed the polymorphic variants and cleaned-up the rebase. @garrigue , would you agree to formally (review and) approve the PR? |
3d1cf26
to
c005973
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one small request, to have no regrets.
typing/outcometree.mli
Outdated
| Oide_ident of string | ||
| Oide_ident of string ref | ||
(** Beware that identifier names can be renamed on the fly to avoid | ||
name collision between different identifiers, see {!Printtyp} *) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to define a new type here, rather than use ref
.
Say, define
type out_name = {mutable printed_name: string}
type out_ident =
...
| Oide_ident of out_name
My concern is mainly about making the code more robust for maintenance, since you end up adding ref
in many places, and this could become hard to track.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A new record type (or maybe an abstract type) does make the intent clearer here indeed. I have pushed a new commit to this effect.
Merged, thanks for the review. |
This change breaks some opam packages, by creating spurious warnings 49. For instance this code, reduced from functoria: module Foo = struct
type info = { doc : unit }
type t = { info : info }
end
let add_extra_info arg = arg.Foo.info.doc
It seems like several things are wrong here, though some of them may predate this change:
|
Interesting! Thanks a lot for the report. |
I will have a look. |
@Octachron don't hesitate to create an MPR to keep track of this problem. |
|
||
(** Same as {!ident_name_simple} but lookup to existing named identifiers | ||
in the current {!printing_env} *) | ||
let ident_name namespace id = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this function needed? I'm refactoring the various lookup functions in the type-checker, and I'm not sure what to do with the one's used to implement env_ident
.
The best I can work out is that this is trying to force us to print t/2
when t
has been shadowed even though we are not printing any references to the shadowing type. This doesn't seem desirable to me, especially printing the associated "conflict" explanation. It's also not implemented correctly anyway.
If it's not really needed I'll just remove it as part of my refactoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is indeed trying to take in account names that have been already bound inside the environment. This is mostly useful when printing signature with ocamlc -i
for cases like
type t = A
module M = struct
type t
let x = A
end
or having meaningful error message slightly earlier. For intance, the code below
type t = A
module M:sig type t val x:t end = struct type t let x = A end;;
raises
Modules do not match:
sig type t val x : t/2 end
is not included in
sig type t val x : t end
Values do not match: val x : t/2 is not included in val x : t/1
without this function, the first part of the error message is useless
Modules do not match:
sig type t val x : t end
is not included in
sig type t val x : t end
Values do not match: val x : t/2 is not included in val x : t/1
If the feature is broken or too hard to implement, I imagine that it could be removed (temporarily or not).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why does this require checking for shadowing? I would have expected the naming context to contain all the t
s that we print and so to add the extra markers in all those cases.
Besides:
Modules do not match:
sig type t val x : t/2 end
is not included in
sig type t val x : t end
Values do not match: val x : t/2 is not included in val x : t/1
doesn't seem to be much of an improvement: is t/1
the same as one of the type t
s or is it some third unrelated type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because when printing a signature, we don't want to print extra-markers if they are not strictly required?
type t
module M = struct
type t = A
val x: t
end
should be printed as
type t
module M: sig
type t = A
val x: t
end
thus checking which t
is in scope and which one is shadowed.
Fix: do not forget some updates in to_cmm_static
MPR#6416, MPR#6634, MPR#4791 ( see also MPR#6323, MPR7402).
This PR aims at getting rid of all instances where different identifiers are printed with the same name in warnings, errors, or inferred signatures; leading to the infamous
and variations.
Since there are many instances where transforming the above message to use valid types is difficult or impossible, this PR takes the easy road and uses unique but not syntactically valid names to disambiguate between identifiers. For instance, the previous error messages becomes:
Note that this naming scheme "identifier.name/identifier.stamp" extends the one already used for unification error messages. These unique names are not particularly clear, but they at least convey the fact there was a problem with some type redefinition, and they could be improved later on.
Moreover, once different identifiers with the same name are disambiguated, it becomes relatively straightforward to detect when a signature printed with
ocamlc -i a.ml
does not corresponds to the real signature of a compilation unit. Therefore, this PR adds a new warning for unprintable signature.For instance, consider the following compilation unit
Currently using
ocamlc -i pr6323.ml
printswhich is the wrong signature, since the true type of
p
is'a Pr6323.t list -> 'a Pr6323.t list
, not'a Dt.t list -> 'a DT.t list
.With this PR applied,
ocamlc -w +63 -i pr6323.ml
yieldsand emits a warning
Implementation overview
On the implementation side, the first commit in this PR introduces a notion of naming context in
printtyp.ml
. This naming context is used to detect name collision between identifiers when buildingoutcome
tree. When such a collision is detected, the identifiers is renamed on the fly to a unique name fo the formidentifier.name / identifier.stamp
.Moreover, a
Deferred
printing submodule make this naming context easier to use by splitting the rendering of types in two distinct phases:First, build all the concerned
outcome
trees, accumulating informationin the naming context
Then, render the outcome tree, once all name collisions have been resolved
Note that this mechanism replaces the specific mechanism used previously for unification error messages.
The second commit make existing error and warning messages uses this new deferred printing module when appropriate.
The third commit introduces the new warning for unprintable signatures, which simply detects if there was any collision event when printing the signature with
ocamlc -i
.The fourth commit extends the naming context collision mechanism to detect name collision with identifiers presents in the printing environment but absent from the current warnings, error messages or signature items. With this extension, most (all?) unprintable signature should be detected.
More examples: