ISO/ IEC JTC1/SC22/WG14 N951


WG14/N951

String literals and concatenation

Clive Feather
<[email protected]>
Last changed 2001-08-14


Introduction
============

There is an inconsistency in the rules for string literal concatenation
and the relationship between source and execution character sets. This
paper discusses this inconsistency and suggests a new model and associated
changes to the Standard.

This paper was written following discussions on the WG14 reflector, with
particular input from Tanaka Keishiro and Antoine Leca.


Standard text
=============

The following text from the Standard is relevant.

Translation phase 1:

     Physical source file multibyte characters are  mapped,
     in  an  implementation-defined  manner,  to the source
     character set  (introducing  new-line  characters  for
     end-of-line   indicators)   if   necessary.

Translation phase 3:

     The  source  file  is  decomposed  into  preprocessing
     tokens   and  sequences  of  white-space  characters
     (including comments).

Translation phase 5:

     Each source character set member and  escape  sequence
     in   character   constants   and  string  literals  is
     converted to the corresponding member of the execution
     character set; if there is no corresponding member, it
     is converted to an implementation-defined member other
     than the null (wide) character.

Translation phase 6:

     Adjacent string literal tokens are concatenated.

6.4.5#1:

     [#1]

     string-literal:
         " s-char-sequence-opt "
         L" s-char-sequence-opt "

     s-char-sequence:
         s-char
         s-char-sequence s-char

     s-char:
         any member of the source character set except the double-quote ",
             backslash \, or new-line character
         escape-sequence

6.4.5#4:

     [#4]   In  translation  phase  6,  the  multibyte  character
     sequences specified by any sequence  of  adjacent  character
     and  wide  string  literal  tokens  are  concatenated into a
     single multibyte character sequence.  If any of  the  tokens
     are  wide  string  literal  tokens,  the resulting multibyte
     character sequence is treated  as  a  wide  string  literal;
     otherwise, it is treated as a character string literal.

6.4.5#5:

     [#5] In translation phase 7, a byte or code of value zero is
     appended to each multibyte character sequence  that  results
     from   a  string  literal  or  literals.  The  multibyte
     character sequence is then used to initialize  an  array  of
     static  storage  duration  and  length  just  sufficient  to
     contain the sequence.  For character  string  literals,  the
     array  elements have type char, and are initialized with the
     individual bytes of the multibyte  character  sequence;  for
     wide  string literals, the array elements have type wchar_t,
     and are initialized with the  sequence  of  wide  characters
     corresponding   to  the  multibyte  character  sequence,  as
     defined by the mbstowcs  function  with  an  implementation-
     defined  current  locale.   The  value  of  a string literal
     containing a multibyte  character  or  escape  sequence  not
     represented    in    the    execution   character   set   is
     implementation-defined.


Problems
========

Consider code like:

     L"abc" "def"

The 6.4.5#4 text says that the multibyte sequences in the literals are
concatenated into a single sequence in translation phase 6. But, on the
other hand, multibyte characters were mapped to source characters in TP1,
and the source characters were then mapped to execution character set
characters in TP5. So there are no multibyte sequences available in TP6 to
be concatenated.

There are then further problems. Consider a string literal containing a
UCN:

     L"\u8868"

At TP5 this is converted to a member of the execution character set, but
at TP7 (6.4.5#5) this literal is supposed to generate a multibyte
character that can be fed to mbstowcs. Nowhere is it explained where this
multibyte character comes from.

Finally, consider an implementation where the two byte sequence 0x95 0x5C
is the source encoding of U+8868. Look at the following literals:

     L"@\"              (@ represents the byte with value 0x95)
     L"\x95\x5C"
     L"\x95" "\\"

At TP5 the second of these is effectively converted to the first, and
after concatenation in TP6 so is the third. This means that all of these
literals generate an array of one element, holding the wide character
with value 0x955C. This is somewhat counter-intuitive, and Tanaka-san
states that it is not what users will expect or implementers will produce.

The alternative is to assume that TP1 will convert the first literal to
some internal character. But in this case TP7 lacks anything obvious to
pass to mbstowcs, and the other two cases still generate the "wrong"
answer.


Some examples of desired output
===============================

Our next step was to consider a range of examples and note what we thought
they should produce.

Example               Array type       Array contents

1:  "ABC"             (char [4])    { 0x41, 0x42, 0x43, 0x00 }
2:  "\x12" "34"       (char [4])    { 0x12, 0x33, 0x34, 0x00 }
3:  "\x95" "\\"       (char [3])    { 0x95, 0x5C, 0x00 }
4:  "@\"              (char [3])    { 0x95, 0x5C, 0x00 }
5:: "@" "\\"          (char [3])    { 0x95, 0x5C, 0x00 } OR UNDEFINED

6:  L"ABC"            (wchar_t [4]) { 0x0041, 0x0042, 0x0043, 0x0000 }
7:  L"\u8868"         (wchar_t [2]) { 0x955C, 0x0000 }
8:  L"\x95\\"         (wchar_t [3]) { 0x0095, 0x005C, 0x0000 }
9:  L"\x95" L"\\"     (wchar_t [3]) { 0x0095, 0x005C, 0x0000 }
10: "\x95" L"\\"      (wchar_t [3]) { 0x0095, 0x005C, 0x0000 }
11: L"@\"             (wchar_t [2]) { 0x955C, 0x0000 }
12: L"\x955C"         (wchar_t [2]) { 0x955C, 0x0000 }
13: L"\x95"           (wchar_t [2]) { 0x0095, 0x0000 }

14: "@\\"             UNDEFINED
15: "@" "\"           UNDEFINED

Example 14 is undefined because \" is an escape sequence and so the
literal is unterminated.

Example 15 depends on whether @" is a valid multibyte sequence or not.
If it is, then the third " terminates the literal and the backslash causes
a syntax error. If it is not, the second literal is unterminated.

Example 5 is defined or undefined in the same way.


Principles
==========

 From consideration of various examples we can derive a set of basic
principles for string literals.

[P1] The sequences:

     L"a" L"b"
     L"a"  "b"
      "a" L"b"

are completely equivalent. The final type of a concatenated string literal
depends only on whether any of the components have an L prefix, and not on
which ones they are.

[P2] The sequences:

     "abc"
     "ab" "c"
     "a" "bc"
     "abc"

are completely equivalent. The division of the string into literals does
not alter the final array. However, this applies only when the literals
consist of the same s-chars; the sequences:

     "\x1234"
     "\x12" "34"

are not equivalent because they involve different s-chars.

[P3] Multibyte sequences are converted to single source characters during
TP1, and so each multibyte sequence is a single s-char.

[P4] The literal "@\" contains one s-char but the literal "\x95\\"
contains two. These are not equivalent, and the latter is not merged to
form a multibyte character later on.

[P5] The two string literals:
      "abc"
     L"abc"
should be related. More precisely, applying mbstowcs to the former should
produce the latter.

[P6] When the final result will be a wchar_t array, each s-char in the
source generates exactly one element of the array.

[P7] When the final result will be a char array:
- a single byte source character generates exactly one byte
- an escape sequence generates exactly one byte
- a non-single byte multibyte source character generates one or more
   bytes, and:
   * mbstowcs applied to the sequence produces a single wide character;
   * where it makes sense, the byte sequence in the array is the direct
     analogue of the source multibyte character.

[P8] When the final result will be a wchar_t array, source shift sequences
are not separate s-chars and do not map to separate elements of the array.

[P9] WHen the final result will be a char array, source shift sequences
should appear in the array to the extent it makes sense (by analogue with
the last sub-bullet of P7).


New model
=========

Applying these principles to the processes in the Standard, we can
construct a new model.

The source character set contains the 95 required characters and the "new
line" indicator. It also contains as many additional characters as are
defined by every valid multibyte character (and making allowance for shift
states).

For example, suppose that a given encoding consists of:
- codes 1 to 96 are the required characters;
- codes 101 to 120 are always followed by a code from 1 to 100, and each
   pair represents a character;
- codes 121 to 127 each represent one of four characters depending on the
   choice of shift state;
- codes 97 to 100 select a shift state; this only affects codes 121 to 127.
Therefore the entire encoding contains 96+20*100+8*4 = 2128 characters,
and that is the size of the source character set.

Translation phase 1 converts all input to characters from this set. Thus
the sequence:

     1  81  81  78  46 100 122 122 101  54
     A   ?   ?   /   t       $   $       `

is converted to the 6 source character sequence A\t$$'

If a source character can be generated in more than one way (e.g. through
the use of alternative shift sequences), an implementation is free to
annotate the character with this information. This annotation is used
later.

Within string literals, these sequences are parsed into s-chars during
TP3; in this case there are 5 such s-chars. Other source code also works
in terms of these source characters.

TP4 stringisation and token pasting works in terms of these source
characters.

The execution character set needs essentially the same set of characters
as the source had. At TP5 each s-char in a string literal is converted to
the corresponding execution character set character. At this point the
distinction between multibyte characters, UCNs, and other escape sequences
is lost (so \t, \x9 (or whatever), and an actual source tab all produce
the same character). At TP6 the sequences of characters are simply
concatenated without further change.

At TP7 each character in the execution character set generates either:
- a single wide character
- a sequence of characters
In the latter case, if the corresponding s-char came from a multibyte
character the sequence should match it if possible. The annotation
mechanism described above is one way to do this.


Proposed changes
================

The following changes to the Standard are required to put this model into
effect.

Firstly we specify this model in some detail:

     5.2.1.3 Character encoding model

     [#1] Translation phase 1 establishes the boundaries between multibyte
     characters in the source. These are converted into /source character
     encoding units/ that encode a single member of the source character
     set (any shift sequences are merged with an adjacent unit). Source
     character encoding units are never split or merged in subsequent
     translation phases.

     [#2] In translation phase 3, each source character encoding unit that
     is not a member of the basic character set will become:
     - an identifier-nondigit within an identifier or pp-number
     - an h-char or q-char in a header-name
     - a c-char within a character-constant
     - an s-char within a string-literal, or
     - a preprocessing-token on its own.

     [#3] In translation phase 5, each c-char or s-char is converted to a
     single /execution character encoding unit/ (ECEU). Each character
     constant and string literal therefore becomes a sequence of ECEUs.
     Note that there may be several representations of the same ECEU:
     - a source character encoding unit, possibly derived from a multibyte
       sequence
     - a universal character name,
     - a special escape sequence such as \t, or
     - an octal or hexadecimal escape sequence

     [#4] In translation phase 6, string literals are concatenated by
     concatenating the ECEU sequences into a single sequence; the
     total number of ECEUs involved is unchanged.

     [#5] In translation phase 7, a string literal is converted to an array
     of values by first appending an ECEU, representing the null
     character, to the ECEU sequence. If it is a character string literal,
     each ECEU then generates one or more elements of the char array; the
     precise elements generated may depend on the source code encoding
     unit that the ECEU derives from. If it is a wide string literal,
     each ECEU generates one element of the wchar_t array.

     [#6] Two character string literals or two wide string literals derived
     from the same sequence of source character encoding units shall
     generate identical arrays. A character string literal and a wide
     string literal derived from the same sequence shall generate arrays
     that correspond, as defined by the mbstowcs function with an
     implementation-defined current locale.

Next we need to make the explanation of string concatenation in 6.4.5#4
to use this new model. This completely replaces the old text:

     [#4] In translation phase 6, the contents of adjacent
     character and wide string literal tokens are concatenated into
     a single token as described in 5.2.1.3. If any of the tokens
     are  wide  string  literal  tokens,  the resulting token is
     a wide string literal; otherwise, it is a character string
     literal.

Finally we need to make the explanation of string literals in 6.4.5#5
also use this new model. Again, this completely replaces the old text.

     [#5] In translation phase 7, a code of value zero (representing the
     null character) is appended to each string literal. The contents of
     the literal are then used to initialize an array of static storage
     duration and length just sufficient to contain the sequence. For
     character string literals, the array elements have type char; for
     wide string literals, the array elements have type wchar_t. The
     array is initialized as described in 5.2.1.3.

[No text replaces the last sentence of the current #5, as it duplicates
a requirement in TP5.]

If it is preferred that 6.4.5#5 not contain the reference to 5.2.1.3,
an alternative way to word the former would be:

     [#5] In translation phase 7, a code of value zero (representing the
     null character) is appended to each string literal. The contents of
     the literal are then used to initialize an array of static storage
     duration and length just sufficient to contain the sequence. For
     character string literals, the array elements have type char; each
     ECEU in the string literal (taken in order) determines the value of
     one or more elements (the precise values may depend on the source
     code encoding unit(s) that the ECEU derives from). For wide string
     literals, the array elements have type wchar_t; each ECEU in the
     string literal determines the initial value for the corresponding
     element.

If so, 5.2.1.3#5 should be deleted and "in translation phase 7" should be
added to the end of the first sentence of 5.2.1.3#6.