Skip to content

Commit da707fb

Browse files
committed
Add additional unicode documentation []
1 parent 51d5795 commit da707fb

1 file changed

Lines changed: 242 additions & 0 deletions

File tree

‎doc/source/lex_pp.rst‎

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,248 @@ top-level modification. (This is the reason that it is not permitted to set
18801880
``MACRO_INVOCATION_TREE_IN_IL`` to TRUE without also setting
18811881
``FULLY_RESOLVED_MACRO_POSITIONS`` to TRUE.)
18821882

1883+
Unicode Version-Dependent Features
1884+
==================================
1885+
1886+
The front end has three distinct features that depend on the definitions
1887+
and classifications of characters in the Unicode standard and thus might
1888+
need to be updated when new versions of Unicode are released. The following
1889+
subsections discuss each of these features and what is involved in a potential
1890+
update.
1891+
1892+
Classification of Identifier Characters
1893+
---------------------------------------
1894+
1895+
The front end must, of course, determine whether a given character can
1896+
appear in a C/C++ identifier in order to find the start and end of
1897+
identifier tokens during lexical analysis. There is also a distinction
1898+
between characters that can begin an identifier and those that can appear
1899+
only after the initial character; for example, ``'0'`` cannot be the first
1900+
character of an identifier but it is valid in the succeeding positions.
1901+
1902+
For single-byte character sets, this classification is done using boolean
1903+
arrays indexed by the character value; see the declarations of
1904+
``is_id_char``, ``is_id_char_no_mbc``, and ``char_ends_id`` in
1905+
``lexical.h`` and their initialization in ``lexical_one_time_init`` in
1906+
``lexical.c`` for details.
1907+
1908+
The situation is more complicated for multi-byte characters, whether
1909+
appearing directly in the source or via a *universal-character-name*. For
1910+
C++23 (and, because the relevant paper was adopted as a defect report, for
1911+
previous versions as well) and C23, the determination is based on the
1912+
Unicode ``XID_Start`` and ``XID_Continue`` Derived Properties as specified
1913+
in `www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt
1914+
<https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt>`__.
1915+
(Specific versions of this file can be found by replacing ``UCD/latest``
1916+
with the version number, e.g., ``15.1.0``, in the URL.) Earlier versions of
1917+
the C and C++ standards used different methods to specify these
1918+
classifications, however, and the front end continues to support those
1919+
variants for backward compatibility.
1920+
1921+
The mechanism for achieving compatibility with multiple classification
1922+
schemes is the ``UCN_table`` array, defined in ``lexical.c``. The elements
1923+
of the array are objects of the following struct:
1924+
1925+
.. code:: c++
1926+
1927+
typedef struct a_UCN_range {
1928+
unsigned long start; /* The first character in the range. */
1929+
unsigned long end; /* The last character in the range. */
1930+
a_byte dialects;
1931+
/* A bit set indicating the dialects for which this
1932+
range of characters is accepted. See below for
1933+
the encoding masks. */
1934+
a_byte non_initial_dialects;
1935+
/* A bit set indicating the dialects for which this
1936+
range of characters is not permitted as the
1937+
first character of an identifier. This uses the
1938+
same encoding as dialects. */
1939+
} a_UCN_range;
1940+
1941+
Characters tend to occur in groups, so to reduce the storage requirements
1942+
of the array, each element describes a contiguous range of characters, all
1943+
of which share the same treatment in all of the supported classification
1944+
schemes. The elements of the array are sorted in strictly-ascending order
1945+
of code point so that the array can be efficiently searched using a binary
1946+
search. The ``dialects`` and ``non_initial_dialects`` members are bit sets
1947+
with each bit representing a different language dialect, specifying how the
1948+
characters in the specified range are classified in the various
1949+
dialects. The C++23/C23 dialect is represented by the ``UAX44`` bit (so
1950+
named because the ``XID_Start`` and ``XID_Continue`` properties are
1951+
described in Unicode Annex #44).
1952+
1953+
From the perspective of updating this table when new versions of Unicode
1954+
are published, the principal requirement of this table is that all members
1955+
of a given character range must be classified the same way in all of the
1956+
supported dialects. A change in the classification of a character in a new
1957+
Unicode version thus typically means that an existing range in the table
1958+
must be split, so that any characters before and/or after the character of
1959+
interest can maintain their previous values while that character's
1960+
``UAX44`` bits reflect the new Unicode classification (maintaining the
1961+
previous values for all the other flags). As a specific example, the
1962+
``DerivedCoreProperties.txt`` file for Unicode version 15.1.0 contained
1963+
(among others) the following changes from version 15.0.0:
1964+
1965+
.. code:: text
1966+
1967+
+2EBF0..2EE5D ; XID_Start # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
1968+
. . .
1969+
+2EBF0..2EE5D ; XID_Continue # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
1970+
1971+
These changes indicate that the characters in the indicated range can now
1972+
appear both within identifiers and as the first character of an
1973+
identifier. These code points were previously contained within the range of
1974+
the following element of the ``UCN_table`` array:
1975+
1976+
.. code:: c++
1977+
1978+
{ 0x2ebe1, 0x2f7ff, _ | _ | CPP11 | _ , _ | _ | _ },
1979+
1980+
As a result, the range needed to be split in order to preserve the
1981+
classification of the characters before and after the new 15.1.0 range,
1982+
resulting in the following three elements to replace the existing one:
1983+
1984+
.. code:: c++
1985+
1986+
1987+
{ 0x2ebe1, 0x2ebef, _ | _ | CPP11 | _ , _ | _ | _ },
1988+
{ 0x2ebf0, 0x2ee5d, _ | _ | CPP11 | UAX44, _ | _ | _ },
1989+
{ 0x2ee5e, 0x2f7ff, _ | _ | CPP11 | _ , _ | _ | _ },
1990+
1991+
Note both the addition of the ``UAX44`` flag in the initializer for
1992+
``dialects`` and its absence in the initializer for
1993+
``non_initial_dialects``, since the latter specifies dialects for which the
1994+
characters are *not* permitted to start an identifier.
1995+
1996+
Detection of Source Code Unicode Vulnerabilities
1997+
------------------------------------------------
1998+
1999+
One potential avenue of source-level exploits relies on the fact that a
2000+
number of distinct Unicode characters have similar or identical glyphs. For
2001+
example, the Latin, Greek, and Cyrillic alphabets each have a character
2002+
whose visual appearance is "A", even though the code points for those
2003+
characters are different. This permits the creation of programs with
2004+
vulnerabilities that cannot be detected by even a careful code review (by
2005+
hijacking overloaded function calls, for example).
2006+
2007+
Unicode provides a table,
2008+
`www.unicode.org/Public/security/latest/confusables.txt
2009+
<https://www.unicode.org/Public/security/latest/confusables.txt>`__, that
2010+
enumerates such potentially-confused characters (replace "latest" with the
2011+
version number to access the specification for a specific version). This
2012+
table is described in
2013+
`www.unicode.org/reports/tr39/ <https://www.unicode.org/reports/tr39/>`__.
2014+
2015+
The front end incorporates this data as the array ``confusable_map``,
2016+
defined in ``lexical.h``, which is a lightly-massaged version of the
2017+
contents of ``confusables.txt``. Each element of the array is an object of
2018+
the following struct:
2019+
2020+
.. code:: c++
2021+
2022+
struct a_confusable_map_elem {
2023+
int src_char; /* The numeric value of a Unicode code point whose
2024+
glyph could be confused with that of another
2025+
code point. */
2026+
int prototype[MAX_PROTOTYPE_LENGTH];
2027+
/* The numeric values of the Unicode code points of
2028+
the prototype character(s) for src_char, as
2029+
defined in unicode.org/reports/tr39, "Unicode
2030+
Security Mechanisms", section 4. */
2031+
}; /* a_confusable_map_elem */
2032+
2033+
The elements are in strictly-ascending order of ``src_char``, to facilitate
2034+
efficient searching using a binary search.
2035+
2036+
Although the entries in ``confusables.txt`` are not in the same order as
2037+
the elements of ``confusable_map``, that does not present a significant
2038+
issue for updating the front end for new Unicode versions, as the mapping
2039+
from the Unicode data to the corresponding array initializer is
2040+
obvious. For example, a representative line of the version 15.1.0 file is:
2041+
2042+
.. code:: text
2043+
2044+
2238 ; 002D 0307 ; MA #* ( ∸ → -̇ ) DOT MINUS → HYPHEN-MINUS, COMBINING DOT ABOVE #
2045+
2046+
and the corresponding initializer element is:
2047+
2048+
.. code:: c++
2049+
2050+
{ 0x02238, { 0x002D, 0x0307 } },
2051+
2052+
Thus, a simple ``diff`` of two versions of the ``confusables.txt`` file
2053+
provides sufficient information for any required updates.
2054+
2055+
Such updates are expected to be infrequent, in any event, as the base data
2056+
is not likely to change very much over time. If desired, the
2057+
``edg-make-confusables-data`` script can be used with a given version of
2058+
``confusables.txt`` to create the entire initializer for
2059+
``confusable_map``.
2060+
2061+
Named Unicode Escapes
2062+
---------------------
2063+
2064+
C++23 introduced the capability of incorporating a given Unicode character
2065+
into the source text by specifying its full name, using the syntax ``\N{``\
2066+
*character-name*\ ``}``. This feature requires the front end to be able to
2067+
distinguish valid *character-name*\ s from invalid ones and to determine the
2068+
code point associated with each valid name.
2069+
2070+
Because Unicode defines tens of thousands of characters, with the average
2071+
name being over thirty characters in length, this task is necessarily
2072+
data-intensive. A naive straightforward approach, e.g., a sorted array of
2073+
character names and code points searched using a binary search, would
2074+
require around 1.5 megabytes of data and up to sixteen probes to match a
2075+
given character name, so it would be fairly inefficient in both data and
2076+
execution time.
2077+
2078+
The approach chosen for the front end implementation of this feature uses a
2079+
fairly straightforward finite state machine (FSM), with each state
2080+
representing the initial substring of characters in one or more character
2081+
names and each transition designating the state that would result for a
2082+
specific character following that initial substring in a valid Unicode
2083+
character name. There are a few simple optimizations to reduce the size of
2084+
the data containing the FSM; sequences of single-transition states are
2085+
compressed into a single state that matches a sequence of characters and
2086+
has a single transition on the final one, and the algorithmically-named
2087+
ranges for CJK and Tangut ideographs are also condensed into single states
2088+
matching the common prefix and specifying their respective ranges of code
2089+
points. Incorporating these optimizations, the storage required for the
2090+
state machine for the names in Unicode 15.1.0 is about 510 kilobytes, much
2091+
less than the naive implementation, and its performance is also much
2092+
better.
2093+
2094+
The data for the Unicode character names and code points comes from two
2095+
files: `www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
2096+
<https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt>`__ and
2097+
`www.unicode.org/Public/UCD/latest/ucd/NameAliases.txt
2098+
<https://www.unicode.org/Public/UCD/latest/ucd/NameAliases.txt>`__. (The
2099+
files for specific versions can be found by substituting the version
2100+
number, e.g., ``15.1.0``, for ``UCD/latest`` in the URLs.)
2101+
2102+
The data for the state machine in the front end is contained in the array
2103+
``unicode_name_fsm``, defined in the file ``unicode_name_fsm.c``. Because
2104+
this table cannot be edited by hand, there is a utility that produces that
2105+
file in its entirety, with no manual editing required. To run the utility,
2106+
download the two files above from the ``unicode.org`` web site and issue
2107+
the following command:
2108+
2109+
``process_unicode_names UnicodeData.txt NameAliases.txt`` *output-file version date*
2110+
2111+
where *output-file* is any file name (it must be copied to
2112+
``unicode_name_fsm.c`` if that file is not updated *in situ*); *version* is
2113+
the Unicode version number (e.g., ``15.1.0``); and *date* is the release
2114+
date of the Unicode version (e.g., ``2023-08-28``). (The latter two
2115+
command-line arguments are incorporated into the comments in the generated
2116+
source file.)
2117+
2118+
The source for the utility is found in the
2119+
``dev_tools/cpp_tools/process_unicode_names`` subdirectory. Any changes
2120+
desired in the contents of ``unicode_name_fsm.c`` (such as updating the
2121+
copyright date) should be edited into the utility source in the
2122+
initializer for the ``header_text`` variable, since any manual edits in
2123+
``unicode_name_fsm.c`` will be overwritten by the next run of the utility.
2124+
18832125
.. [#f1] Trigraphs and line splices are not undone and so do not appear in the
18842126
output.
18852127
.. [#f2] As mentioned previously, the actual newline character is replaced by

0 commit comments

Comments
 (0)