-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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
proposal: spec: define identifiers to be NFKC-normalized #27896
Comments
Related issues:
|
Unicode has specific recommendations for identifiers at http://unicode.org/reports/tr31/ . |
@ianlancetaylor Thanks for the reference. From that document, it seems like what I want is UAX31-R4 (Equivalent Normalized Identifiers), which implies that we also need the NFKC modifications. |
One downside of choosing NFKC over NFC is that my cutesy subscript variables like |
If I may propose a slight modification, gofmt should rewrite all identifiers to the form used in a definition/declaration. If something like |
That does sound nicer, but also much more difficult to implement (since it requires semantic information about variable scopes). But perhaps more importantly, it wouldn't play as nicely with command-line search tools (such as |
The compiler should already have that information -- where an object is defined. It now just has to save a "display" string, which can only be set at the point of defn. This is in addition to a normalized identifier. As for search tools, it would be somewhat similar to how option -i is handled. Just as |
Go is used on a wide variety of platforms: the Go team cannot unilaterally change core tools on those platforms. (Even if we could, it would be an enormous amount of work.) |
Another possible variant: make it an error to declare two NFKC-equivalent identifiers in the same scope, and say that an identifier declared in an inner scope shadows any NFKC-equivalent identifiers from outer scopes, but continue to require that all uses of an identifier exactly match the form it was declared with. Then we get the following desirable properties:
|
A simple solution would be to disallow any character with a decomposition type "font" and permit all others as is. This would allow the cutesy aᵢ, but avoid the issue pointed out by @andersk. However, disallowing or normalizing to NFKC is not backwards compatible. |
@bcmills: in general, normalizing identifiers to NFKC is not a good idea. NFKC normalization has a tendency to break properties of strings, meaning that if a string has some property before NFKC normalization, NFKC normalization may break it. Handling such cases properly can be hard. |
Could you elaborate on how that impacts identifiers specifically? (I'm not proposing that we normalize Go source overall — only identifiers.) |
I don't see how those properties can both hold. |
Here's a neat example to consider (https://play.golang.org/p/NX3DW0YIGmE), because of an unfortunate interaction with permissive redeclaration (see also #377):
If identifiers are not compatibility-normalized, then |
@bcmills: re: impact of NFKC normalization of identifiers: TR31 points this out quite nicely. In order to make NFKC work properly for this case, it is recommended once implements the modifications mentioned in http://unicode.org/reports/tr31/#NFKC_Modifications. So luckily here it is hashed out, but if one wants to augment the spec for identifiers in any way, one needs to consider what this entails. Simply disallowing runes with a decomposition type "font" is simpler and gets the best of both worlds (avoids the problem you mention and also allows using |
But note that even disallowing runes with a decomposition type "font" only eliminates a small portion of such possible confusion. For instance, it would still allow the use of More feasible would be writing a vet tool that detects cases of confusability. |
@mpvl, I don't see how disallowing runes with decomposition type “font” helps at all. |
That's #20115, but that only solves part of the problem: it flags confusing code once it is already written, but doesn't allow someone with a “reasonable” international keyboard layout to, for example, reliably type an identifier that they've just read in other documentation, even if they know what language it was written in and are using characters appropriate to that language. |
Compat could be another candidate, indeed. No need to decompose "circle", "sub", "super", etc., though. Aside from which to include or not, the point is that disallowing decomposing runes deemed illegal for identifiers is better than silently decomposing them. It avoids implementation complexity and is clearer overall. |
With this normalization will ids like "рое" & "poe" be considered equivalent? The first "рое" is in Cyrillic. |
@bakul, no. Normalization doesn't convert between Cyrillic and Latin. |
Background
Go 1 allows Unicode letters as part of identifiers.
Normally that's quite useful, but sometimes it can lead to confusion: some letters look like and are acceptable substitutes for other similar letters.¹
For example, in this code snippet, the declared identifier is U+00B5 (MICRO SIGN), which is
AltGr+m
on the Linuxaltgr-intl
layout, while its use is U+03BC (GREEK SMALL LETTER MU), which is on the long-press menu for theπ
key on the Google Gboard on-screen keyboard for Android and iOS.Fortunately, the Unicode standard defines a number of normalization forms that are intended to smooth out these differences. As it happens,
µ
andμ
are equivalent under the “compatibility” forms NFKC and NFKD.Proposal
I propose that in Go 2, identifiers with the same NFKC normal form (with identifier modifications) should be considered the same identifier, and
gofmt
should automatically rewrite all identifiers within a program to their normal forms.Compatibility
This change is not strictly Go 1 compatible: a Go 1 program may, for example, define and use distinct variables named
µ
andμ
in the same scope. I argue that such programs are confusing at best, and actively misleading at worst, so the only programs that would break under this proposal should be fixed regardless of it.(CC @mpvl @griesemer @robpike)
¹ In the example here, U+03BC is preferred over U+00B5: see https://unicode.org/reports/tr25/.
Revisions to this proposal:
The text was updated successfully, but these errors were encountered: