Skip to main content

rucc_lex/
literal.rs

1//! Character constants and string literals: the escapes, the encoding prefixes, and what the
2//! elements end up being.
3//!
4//! Design: `spec/06-lexer-and-parser.md` section 6.1.
5//!
6//! This is the last piece of phase 7 that is about spellings. A literal arrives here as the
7//! bytes the user wrote, prefix and quotes included, and leaves as elements: one value per
8//! element of the array a string is, or one number for the character constant. What an element
9//! is depends on the prefix and on the target, which is most of the work.
10//!
11//! The execution character set is UTF-8 and the wide one is UTF-32, or UTF-16 where `wchar_t`
12//! is sixteen bits, which is Windows. That is what both compilers default to and it is the only
13//! choice that makes a UTF-8 source file mean what it looks like. `-fexec-charset` is not
14//! implemented and would change these answers if it ever is.
15//!
16//! # What an escape is worth
17//!
18//! There are two kinds of escape and the difference matters more than it looks. `é` names
19//! a character, so it is encoded in whatever the literal's encoding is, and in a plain string it
20//! becomes the two bytes `c3 a9`. `\xe9` is a value, not a character, so it is that one element
21//! and nothing encodes it. gcc 13.3 agrees on both: `"é"` is three bytes long and
22//! `"\xe9"` is two.
23//!
24//! A value escape that does not fit its element is truncated with a warning, and the element is
25//! what decides, not the type: `u8'\xff'` is fine and `'\x1ff'` is not, and both are eight bits
26//! wide. Octal runs to three digits and stops, so `"\1234"` is `S4` and not one escape, while
27//! hexadecimal runs as far as there are hexadecimal digits.
28//!
29//! An escape whose letter means nothing is that letter, with a warning, so `'\q'` is `'q'`.
30//! `\e` is the escape character in both compilers and in no standard. `\N{NAME}` is refused,
31//! because gcc 13.3 has it in C++23 alone and inventing our own answer would be worse.
32//!
33//! # Universal character names
34//!
35//! A UCN may not name a character in the basic character set, so a UCN that spells out the
36//! letter `A` is an error even though `'A'` is a constant. The exception is the three characters
37//! `$`, `@` and the backquote, which are below a space in the table and allowed anyway.
38//! Surrogates are refused. gcc still reports the basic character case in C23, where the standard
39//! relaxed it, so this follows gcc and not the paper. Before C99 a UCN is converted with a remark
40//! rather than refused, which is also what gcc does.
41//!
42//! A code point above `\U0010ffff` is an error here and a warning in gcc, which then encodes
43//! the value as though UTF-8 went that far. clang refuses it, this refuses it, and it is the
44//! one deliberate difference in this module.
45//!
46//! # Character constants
47//!
48//! A plain character constant is an `int` and not a `char`, and its value is the character
49//! converted to `int`, so `'\xff'` is minus one where plain `char` is signed and 255 where it
50//! is not. More than one character is implementation defined and both compilers shift them
51//! together, so `'ab'` is `0x6162`, with a warning. Past the width of the type the ones at the
52//! front fall off, so `'abcde'` is `0x62636465`, and gcc says "too long" there instead of
53//! "multi-character" rather than as well as it. A wide constant has room for exactly one, so
54//! `L'ab'` is `'b'` with the same warning, and a `u8` constant with two characters is an error
55//! rather than a warning. All measured on gcc 13.3, x86-64 Linux.
56//!
57//! # The prefixes and the dialects
58//!
59//! `L` is C89, `u` and `U` are C11, `u8` on a string is C11 and `u8` on a character constant is
60//! C23. In an older dialect gcc lexes `u8'a'` as the identifier `u8` followed by a character
61//! constant, which is a different token stream rather than a different constant. The scanner
62//! here reads it as one token in every dialect, which is the simpler rule and a divergence in
63//! the token stream that no real program can see, so the dialect is checked at this point
64//! instead and the constant is refused with a message that names the dialect it needs.
65
66use rucc_session::Std;
67use rucc_target::TargetInfo;
68
69use crate::remarks::Remarks;
70
71/// The encoding prefix of a character constant or a string literal.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum Encoding {
74    /// No prefix, whose element is a `char`.
75    Plain,
76    /// `L`, whose element is a `wchar_t` and so is a fact about the target.
77    Wide,
78    /// `u8`, whose element is a `char8_t`, which is an `unsigned char`.
79    Utf8,
80    /// `u`, whose element is a `char16_t`, which is a `uint_least16_t`.
81    Utf16,
82    /// `U`, whose element is a `char32_t`, which is a `uint_least32_t`.
83    Utf32,
84}
85
86impl Encoding {
87    /// The width of one element in bits.
88    #[must_use]
89    pub fn element_width(self, target: &TargetInfo) -> u32 {
90        match self {
91            Encoding::Plain | Encoding::Utf8 => 8,
92            Encoding::Wide => target.wchar_width,
93            Encoding::Utf16 => 16,
94            Encoding::Utf32 => 32,
95        }
96    }
97
98    /// Whether the element type is signed, which only `char` and `wchar_t` can be.
99    #[must_use]
100    pub fn is_signed(self, target: &TargetInfo) -> bool {
101        match self {
102            Encoding::Plain => target.char_is_signed,
103            Encoding::Wide => target.wchar_is_signed,
104            Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => false,
105        }
106    }
107
108    /// The prefix this encoding is written with, which is empty for a plain literal.
109    #[must_use]
110    pub const fn prefix(self) -> &'static str {
111        match self {
112            Encoding::Plain => "",
113            Encoding::Wide => "L",
114            Encoding::Utf8 => "u8",
115            Encoding::Utf16 => "u",
116            Encoding::Utf32 => "U",
117        }
118    }
119
120    /// The prefix a spelling was written with, for a caller that needs the encoding of a
121    /// literal it could not convert.
122    #[must_use]
123    pub fn read_prefix(text: &str) -> Encoding {
124        Encoding::read(text.as_bytes()).0
125    }
126
127    /// The prefix at the front of a spelling, and how many bytes it took.
128    fn read(bytes: &[u8]) -> (Encoding, usize) {
129        match bytes {
130            [b'u', b'8', ..] => (Encoding::Utf8, 2),
131            [b'u', ..] => (Encoding::Utf16, 1),
132            [b'U', ..] => (Encoding::Utf32, 1),
133            [b'L', ..] => (Encoding::Wide, 1),
134            _ => (Encoding::Plain, 0),
135        }
136    }
137
138    /// The first dialect that has this prefix, which is not the same for a character constant
139    /// as for a string literal.
140    fn since(self, character: bool) -> Std {
141        match self {
142            Encoding::Plain | Encoding::Wide => Std::C89,
143            Encoding::Utf8 if character => Std::C23,
144            Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => Std::C11,
145        }
146    }
147}
148
149/// A converted character constant.
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub struct CharConstant {
152    /// The value, already converted to the constant's type, which is why it can be negative:
153    /// `'\xff'` is minus one where plain `char` is signed.
154    pub value: i64,
155    /// The prefix it was written with.
156    pub encoding: Encoding,
157    /// What is worth saying about it, for the caller that holds the span.
158    pub remarks: Remarks,
159}
160
161/// A converted string literal.
162#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct StringLiteral {
164    /// One value per element of the array, without the terminating zero, since the zero belongs
165    /// to the type and not to the spelling. An element is a byte in a plain or `u8` literal, a
166    /// UTF-16 code unit in a `u` one, and a code point in a `U` one.
167    pub elements: Vec<u32>,
168    /// The prefix it was written with.
169    pub encoding: Encoding,
170    /// What is worth saying about it, for the caller that holds the span.
171    pub remarks: Remarks,
172}
173
174impl StringLiteral {
175    /// The bytes this literal becomes in the object, terminator included, in the target's byte
176    /// order.
177    #[must_use]
178    pub fn bytes(&self, target: &TargetInfo) -> Vec<u8> {
179        let width = self.encoding.element_width(target) / 8;
180        let mut bytes = Vec::with_capacity((self.elements.len() + 1) * width as usize);
181        for element in self.elements.iter().copied().chain([0]) {
182            let taken = &element.to_le_bytes()[..width as usize];
183            if target.little_endian {
184                bytes.extend_from_slice(taken);
185            } else {
186                bytes.extend(taken.iter().rev());
187            }
188        }
189        bytes
190    }
191}
192
193/// Why a spelling is not a literal.
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum LiteralError {
196    /// The spelling is not a character constant or a string literal at all, which the caller
197    /// cannot reach from a token the scanner produced.
198    NotALiteral,
199    /// `''`, which has no character in it to have a value.
200    Empty,
201    /// More characters than the type has room for, in the one encoding where that is an error
202    /// rather than a warning.
203    TooLong,
204    /// `\x` with no hexadecimal digit after it.
205    NoHexDigits,
206    /// A universal character name that stops before its digits are done.
207    IncompleteUcn,
208    /// A universal character name that may not name what it names: a character in the basic
209    /// character set, a surrogate, or a code point past the end of Unicode.
210    InvalidUcn,
211    /// `\N{NAME}`, which gcc 13.3 has in C++23 and in no C dialect.
212    NamedUcn,
213    /// A byte in the source that is not part of a character, in a literal whose encoding has to
214    /// know what the characters are.
215    InvalidUtf8,
216    /// An encoding prefix the dialect does not have.
217    PrefixNotInDialect,
218    /// A run of adjacent string literals written with two different prefixes, which neither
219    /// compiler has an answer for.
220    MixedEncodings,
221}
222
223impl LiteralError {
224    /// What to print, in GCC's words where GCC has any.
225    #[must_use]
226    pub const fn message(self) -> &'static str {
227        match self {
228            LiteralError::NotALiteral => "not a character constant or a string literal",
229            LiteralError::Empty => "empty character constant",
230            LiteralError::TooLong => "character constant too long for its type",
231            LiteralError::NoHexDigits => "\\x used with no following hex digits",
232            LiteralError::IncompleteUcn => "incomplete universal character name",
233            LiteralError::InvalidUcn => "not a valid universal character",
234            LiteralError::NamedUcn => "named universal character escapes are not supported yet",
235            LiteralError::InvalidUtf8 => "failure to convert the source to the execution charset",
236            LiteralError::PrefixNotInDialect => {
237                "this encoding prefix is not available in this dialect"
238            }
239            LiteralError::MixedEncodings => {
240                "unsupported non-standard concatenation of string literals"
241            }
242        }
243    }
244}
245
246/// Converts the spelling of a character constant into a value.
247///
248/// # Errors
249///
250/// [`LiteralError`], for a spelling that is not a character constant or that holds an escape
251/// that is not one.
252pub fn character(text: &str, std: Std, target: &TargetInfo) -> Result<CharConstant, LiteralError> {
253    let (encoding, body) = open(text, b'\'', std, true)?;
254    let width = encoding.element_width(target);
255    let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
256
257    // The elements are shifted together into one number, which is what both compilers do with a
258    // constant holding more than one, and the ones that fall off the top are the ones the
259    // warning is about.
260    let mut value: u64 = 0;
261    let mut count = 0u32;
262    while let Some(piece) = reader.next(width)? {
263        for element in piece.elements(width) {
264            value = (value << width) | u64::from(element);
265            count += 1;
266        }
267    }
268    let mut remarks = reader.remarks;
269
270    // A plain constant is an `int` however many characters it holds, and every other kind is
271    // exactly one element wide, so that is how many characters fit.
272    let type_width = if encoding == Encoding::Plain { 32 } else { width };
273    let capacity = type_width / width;
274    match count {
275        0 => return Err(LiteralError::Empty),
276        1 => {}
277        _ if encoding == Encoding::Utf8 => return Err(LiteralError::TooLong),
278        _ if count > capacity => remarks = remarks.with(Remarks::TOO_LONG),
279        _ => remarks = remarks.with(Remarks::MULTICHARACTER),
280    }
281
282    // One character is converted to the constant's type from the element's, which is where the
283    // sign of a plain `char` gets in. More than one is already a number of the constant's type
284    // and nothing sign extends it from any narrower width.
285    let (bits, signed) = if count == 1 {
286        (width, encoding.is_signed(target))
287    } else {
288        (type_width, encoding == Encoding::Plain || encoding.is_signed(target))
289    };
290    Ok(CharConstant { value: narrow(value, bits, signed), encoding, remarks })
291}
292
293/// Converts the spelling of a string literal into its elements.
294///
295/// # Errors
296///
297/// [`LiteralError`], for a spelling that is not a string literal or that holds an escape that
298/// is not one.
299pub fn string(text: &str, std: Std, target: &TargetInfo) -> Result<StringLiteral, LiteralError> {
300    strings(std::slice::from_ref(&text), std, target)
301}
302
303/// Converts a run of adjacent string literals into the one literal they are.
304///
305/// The encoding of the result is the prefixed one when any of them is prefixed, so `L"a" "b"`
306/// and `"a" L"b"` are both wide, and two different prefixes in one run is an error rather than
307/// a choice. That is measured on gcc 13.3, which puts it exactly that way: "unsupported
308/// non-standard concatenation of string literals".
309///
310/// The bodies are read in the encoding of the whole run rather than each in its own, which
311/// matters for a character rather than an escape: the accented letter in `L"a" "e-acute"` is
312/// one wide element and not the two bytes it would have been on its own.
313///
314/// # Errors
315///
316/// [`LiteralError`], for a spelling that is not a string literal, a run mixing two prefixes, or
317/// an escape that is not one.
318pub fn strings(
319    texts: &[&str],
320    std: Std,
321    target: &TargetInfo,
322) -> Result<StringLiteral, LiteralError> {
323    let mut bodies = Vec::with_capacity(texts.len());
324    let mut encoding = Encoding::Plain;
325    for text in texts {
326        let (found, body) = open(text, b'"', std, false)?;
327        if found != Encoding::Plain {
328            if encoding != Encoding::Plain && encoding != found {
329                return Err(LiteralError::MixedEncodings);
330            }
331            encoding = found;
332        }
333        bodies.push(body);
334    }
335
336    let width = encoding.element_width(target);
337    let mut elements = Vec::new();
338    let mut remarks = Remarks::NONE;
339    for body in bodies {
340        let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
341        while let Some(piece) = reader.next(width)? {
342            elements.extend(piece.elements(width));
343        }
344        remarks = remarks.with(reader.remarks);
345    }
346    Ok(StringLiteral { elements, encoding, remarks })
347}
348
349/// Reads the prefix and the quotes, and hands back the encoding and what is between them.
350fn open(
351    text: &str,
352    quote: u8,
353    std: Std,
354    character: bool,
355) -> Result<(Encoding, &[u8]), LiteralError> {
356    let bytes = text.as_bytes();
357    let (encoding, prefix) = Encoding::read(bytes);
358    if std < encoding.since(character) {
359        return Err(LiteralError::PrefixNotInDialect);
360    }
361    let rest = &bytes[prefix..];
362    match rest {
363        [first, .., last] if *first == quote && *last == quote => {
364            Ok((encoding, &rest[1..rest.len() - 1]))
365        }
366        _ => Err(LiteralError::NotALiteral),
367    }
368}
369
370/// Cuts a value down to `bits` and reads it back as signed or unsigned.
371fn narrow(value: u64, bits: u32, signed: bool) -> i64 {
372    let masked = if bits >= 64 { value } else { value & ((1u64 << bits) - 1) };
373    if signed && bits < 64 && masked >> (bits - 1) & 1 == 1 {
374        // The bits above the width are the sign, which is what makes `'\xff'` minus one.
375        (masked | !((1u64 << bits) - 1)) as i64
376    } else {
377        masked as i64
378    }
379}
380
381/// One piece of a literal, before it is turned into elements.
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383enum Piece {
384    /// A character, which the encoding has to encode: a character from the source or one named
385    /// by a universal character name.
386    Char(u32),
387    /// A value written as a value, with `\x` or with octal digits, which is one element as
388    /// written and is not encoded.
389    Value(u32),
390}
391
392impl Piece {
393    /// The elements this piece becomes in an encoding of the given element width.
394    fn elements(self, width: u32) -> Vec<u32> {
395        let code = match self {
396            Piece::Value(value) => return vec![value],
397            Piece::Char(code) => code,
398        };
399        match width {
400            8 => {
401                let mut buffer = [0u8; 4];
402                let text = char::from_u32(code)
403                    .map(|character| character.encode_utf8(&mut buffer).len())
404                    .unwrap_or(0);
405                buffer[..text].iter().map(|&byte| u32::from(byte)).collect()
406            }
407            // UTF-16 is the one encoding where a character can take two elements, which is why
408            // a wide string is not the same length on Windows as it is anywhere else.
409            16 if code > 0xffff => {
410                let value = code - 0x1_0000;
411                vec![0xd800 + (value >> 10), 0xdc00 + (value & 0x3ff)]
412            }
413            _ => vec![code],
414        }
415    }
416}
417
418/// Reads the body of a literal one character at a time.
419struct Reader<'a> {
420    /// What is between the quotes.
421    bytes: &'a [u8],
422    /// How far in the reader is.
423    index: usize,
424    /// The dialect, which decides what is worth a remark.
425    std: Std,
426    /// What the literal has earned so far.
427    remarks: Remarks,
428}
429
430impl Reader<'_> {
431    /// The next piece, or [`None`] at the end of the literal.
432    fn next(&mut self, width: u32) -> Result<Option<Piece>, LiteralError> {
433        let Some(&byte) = self.bytes.get(self.index) else {
434            return Ok(None);
435        };
436        self.index += 1;
437        if byte == b'\\' {
438            return self.escape(width).map(Some);
439        }
440        if byte < 0x80 {
441            return Ok(Some(Piece::Char(u32::from(byte))));
442        }
443        // A byte above ASCII begins a character in the source, which is UTF-8. A narrow literal
444        // is UTF-8 too, so its bytes go through untouched and nothing has to be able to decode
445        // them; a wide one has to know which character this is before it can encode it again.
446        if width == 8 {
447            return Ok(Some(Piece::Value(u32::from(byte))));
448        }
449        // Only this one character is decoded, and not the rest of the literal, because what
450        // comes after it may be an escape holding a byte that no character begins with.
451        let length = utf8_length(byte).ok_or(LiteralError::InvalidUtf8)?;
452        let end = self.index - 1 + length;
453        let text = self
454            .bytes
455            .get(self.index - 1..end)
456            .and_then(|slice| std::str::from_utf8(slice).ok())
457            .ok_or(LiteralError::InvalidUtf8)?;
458        let character = text.chars().next().ok_or(LiteralError::InvalidUtf8)?;
459        self.index = end;
460        Ok(Some(Piece::Char(character as u32)))
461    }
462
463    /// The piece an escape sequence is worth, with the backslash already read.
464    fn escape(&mut self, width: u32) -> Result<Piece, LiteralError> {
465        let Some(&byte) = self.bytes.get(self.index) else {
466            // The scanner reports the missing quote, and there is nothing here to convert.
467            return Err(LiteralError::NotALiteral);
468        };
469        self.index += 1;
470        let simple = match byte {
471            b'n' => Some(0x0a),
472            b't' => Some(0x09),
473            b'r' => Some(0x0d),
474            b'a' => Some(0x07),
475            b'b' => Some(0x08),
476            b'f' => Some(0x0c),
477            b'v' => Some(0x0b),
478            b'\\' | b'\'' | b'"' | b'?' => Some(u32::from(byte)),
479            _ => None,
480        };
481        if let Some(value) = simple {
482            return Ok(Piece::Value(value));
483        }
484        match byte {
485            // The escape character, which both compilers have and no standard does.
486            b'e' | b'E' => {
487                self.remarks = self.remarks.with(Remarks::NON_ISO_ESCAPE);
488                Ok(Piece::Value(0x1b))
489            }
490            b'0'..=b'7' => Ok(Piece::Value(self.octal(byte, width))),
491            b'x' => self.hex(width).map(Piece::Value),
492            b'u' | b'U' => self.ucn(byte).map(Piece::Char),
493            b'N' => Err(LiteralError::NamedUcn),
494            // An escape that means nothing is the character itself, which both compilers do
495            // after a warning rather than refusing the program.
496            _ => {
497                self.remarks = self.remarks.with(Remarks::UNKNOWN_ESCAPE);
498                Ok(Piece::Value(u32::from(byte)))
499            }
500        }
501    }
502
503    /// An octal escape, which is at most three digits however many follow, so that `"\1234"` is
504    /// two characters.
505    fn octal(&mut self, first: u8, width: u32) -> u32 {
506        let mut value = u32::from(first - b'0');
507        for _ in 0..2 {
508            match self.bytes.get(self.index) {
509                Some(&byte @ b'0'..=b'7') => {
510                    value = value * 8 + u32::from(byte - b'0');
511                    self.index += 1;
512                }
513                _ => break,
514            }
515        }
516        self.fit(value, width, Remarks::OCTAL_ESCAPE_OUT_OF_RANGE)
517    }
518
519    /// A hexadecimal escape, which runs as far as there are hexadecimal digits.
520    fn hex(&mut self, width: u32) -> Result<u32, LiteralError> {
521        let mut value: u64 = 0;
522        let mut digits = 0;
523        while let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) {
524            // A value far past the width is truncated anyway, so the accumulator stops growing
525            // rather than overflowing, and the remark still gets made.
526            value = value.saturating_mul(16).saturating_add(u64::from(digit));
527            digits += 1;
528            self.index += 1;
529        }
530        if digits == 0 {
531            return Err(LiteralError::NoHexDigits);
532        }
533        Ok(self.fit(
534            u32::try_from(value).unwrap_or(u32::MAX),
535            width,
536            Remarks::HEX_ESCAPE_OUT_OF_RANGE,
537        ))
538    }
539
540    /// A universal character name, with its `u` or `U` already read.
541    fn ucn(&mut self, marker: u8) -> Result<u32, LiteralError> {
542        if self.bytes.get(self.index) == Some(&b'{') {
543            return Err(LiteralError::NamedUcn);
544        }
545        let digits = if marker == b'u' { 4 } else { 8 };
546        let mut value: u32 = 0;
547        for _ in 0..digits {
548            let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) else {
549                return Err(LiteralError::IncompleteUcn);
550            };
551            value = value * 16 + digit;
552            self.index += 1;
553        }
554        // The basic character set is off limits, and so is everything below ` ` except the
555        // three characters the standard lets through. gcc still says so in C23, where the
556        // wording was relaxed, so this follows the compiler rather than the paper.
557        let allowed_low = matches!(value, 0x24 | 0x40 | 0x60);
558        if (value < 0xa0 && !allowed_low) || (0xd800..=0xdfff).contains(&value) || value > 0x10ffff
559        {
560            return Err(LiteralError::InvalidUcn);
561        }
562        if self.std < Std::C99 {
563            self.remarks = self.remarks.with(Remarks::UCN);
564        }
565        Ok(value)
566    }
567
568    /// Cuts an escape down to the element it is written in, and says so when that loses
569    /// something. Which remark that is depends on how the escape was written, because GCC words
570    /// the hexadecimal case and the octal one differently.
571    fn fit(&mut self, value: u32, width: u32, out_of_range: Remarks) -> u32 {
572        if width >= 32 {
573            return value;
574        }
575        let mask = (1u32 << width) - 1;
576        if value & !mask != 0 {
577            self.remarks = self.remarks.with(out_of_range);
578        }
579        value & mask
580    }
581}
582
583/// The value of a hexadecimal digit, and [`None`] when the byte is not one.
584fn hex_digit(byte: u8) -> Option<u32> {
585    char::from(byte).to_digit(16)
586}
587
588/// How many bytes the character starting with this one takes, and [`None`] when no character
589/// starts with it.
590fn utf8_length(byte: u8) -> Option<usize> {
591    match byte {
592        0x00..=0x7f => Some(1),
593        0xc2..=0xdf => Some(2),
594        0xe0..=0xef => Some(3),
595        0xf0..=0xf4 => Some(4),
596        _ => None,
597    }
598}
599
600#[cfg(test)]
601mod tests {
602    use rucc_target::Triple;
603
604    use super::*;
605
606    fn linux() -> TargetInfo {
607        TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
608    }
609
610    fn windows() -> TargetInfo {
611        TargetInfo::new("x86_64-pc-windows-msvc".parse::<Triple>().expect("a known triple"))
612    }
613
614    fn arm() -> TargetInfo {
615        TargetInfo::new("aarch64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
616    }
617
618    /// The value of a character constant on x86-64 Linux, in C23.
619    fn ch(text: &str) -> i64 {
620        character(text, Std::C23, &linux()).expect("a character constant").value
621    }
622
623    /// The remarks a character constant earns on x86-64 Linux, in C23.
624    fn ch_remarks(text: &str) -> Remarks {
625        character(text, Std::C23, &linux()).expect("a character constant").remarks
626    }
627
628    /// What a character constant goes wrong with.
629    fn ch_error(text: &str) -> LiteralError {
630        character(text, Std::C23, &linux()).expect_err("not a character constant")
631    }
632
633    /// The elements of a string literal on x86-64 Linux, in C23.
634    fn str_elements(text: &str) -> Vec<u32> {
635        string(text, Std::C23, &linux()).expect("a string literal").elements
636    }
637
638    /// The bytes a string literal becomes on x86-64 Linux, terminator included.
639    fn str_bytes(text: &str) -> Vec<u8> {
640        string(text, Std::C23, &linux()).expect("a string literal").bytes(&linux())
641    }
642
643    #[test]
644    fn the_ordinary_cases_are_the_characters_they_look_like() {
645        assert_eq!(ch("'a'"), 0x61);
646        assert_eq!(ch(r"'\n'"), 0x0a);
647        assert_eq!(ch(r"'\0'"), 0);
648        assert_eq!(ch(r"'\\'"), 0x5c);
649        assert_eq!(ch(r"'\''"), 0x27);
650        assert_eq!(ch(r#"'\"'"#), 0x22);
651        assert_eq!(ch(r"'\?'"), 0x3f);
652        assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
653    }
654
655    /// A single character in a plain constant goes through plain `char` on the way to `int`,
656    /// which is the whole reason `'\xff'` is a negative number on one target and a positive
657    /// one on another.
658    #[test]
659    fn a_high_character_takes_the_sign_of_plain_char() {
660        assert_eq!(ch(r"'\xff'"), -1);
661        assert_eq!(ch(r"'\377'"), -1);
662        assert_eq!(character(r"'\xff'", Std::C23, &arm()).expect("a constant").value, 255);
663        // Not a `char`, so nothing sign extends it.
664        assert_eq!(ch(r"u8'\xff'"), 255);
665    }
666
667    /// Measured on GCC 13.3, x86-64 Linux. A value escape is truncated to its element and the
668    /// warning is about the truncation, not about the type. The two spellings get two remarks
669    /// because GCC gives them two wordings.
670    #[test]
671    fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
672        let out = character(r"'\x1ff'", Std::C23, &linux()).expect("a constant");
673        assert_eq!(out.value, -1);
674        assert!(out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
675        let out = character(r"'\400'", Std::C23, &linux()).expect("a constant");
676        assert_eq!(out.value, 0);
677        assert!(out.remarks.has(Remarks::OCTAL_ESCAPE_OUT_OF_RANGE));
678        assert!(!out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
679        // Wide enough to hold it, so there is nothing to say.
680        assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
681        assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
682    }
683
684    /// Measured on GCC 13.3: a plain literal in a run takes the prefix of its neighbour, two
685    /// different prefixes are an error, and the bodies are read in the encoding of the run, so
686    /// a character in the plain part is one wide element rather than its UTF-8 bytes.
687    #[test]
688    fn adjacent_literals_agree_on_one_encoding_or_none_at_all() {
689        let target = linux();
690        let wide = strings(&[r#"L"a""#, r#""b""#], Std::C23, &target).expect("a string");
691        assert_eq!(wide.encoding, Encoding::Wide);
692        assert_eq!(wide.elements, vec![0x61, 0x62]);
693        assert_eq!(wide.bytes(&target).len(), 12);
694        let other_way = strings(&[r#""a""#, r#"L"b""#], Std::C23, &target).expect("a string");
695        assert_eq!(other_way.encoding, Encoding::Wide);
696        assert_eq!(other_way.bytes(&target).len(), 12);
697
698        let u8_run = strings(&[r#"u8"a""#, r#""b""#], Std::C23, &target).expect("a string");
699        assert_eq!(u8_run.encoding, Encoding::Utf8);
700        assert_eq!(u8_run.bytes(&target).len(), 3);
701
702        // The plain part is read as wide, so the accented letter is one element and not two.
703        let mixed = strings(&[r#"L"a""#, r#""é""#], Std::C23, &target).expect("a string");
704        assert_eq!(mixed.elements, vec![0x61, 0xe9]);
705
706        for run in [[r#"u8"a""#, r#"u"b""#], [r#"u8"a""#, r#"L"b""#], [r#"u"a""#, r#"L"b""#]] {
707            assert_eq!(
708                strings(&run, Std::C23, &target).expect_err("two prefixes in one run"),
709                LiteralError::MixedEncodings
710            );
711        }
712
713        // A run of one is the same thing as the literal on its own.
714        assert_eq!(
715            strings(&[r#""hi""#], Std::C23, &target).expect("a string").elements,
716            vec![0x68, 0x69]
717        );
718    }
719
720    /// Also measured on GCC 13.3. The characters are shifted together, the ones past the width
721    /// of the type fall off the front, and past that point GCC says "too long" instead of
722    /// "multi-character" rather than as well as it.
723    #[test]
724    fn more_than_one_character_shifts_them_together() {
725        assert_eq!(ch("'ab'"), 0x6162);
726        assert_eq!(ch("'abc'"), 0x616263);
727        assert_eq!(ch("'abcd'"), 0x61626364);
728        assert_eq!(ch("'abcde'"), 0x62636465);
729        assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
730        assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
731        assert_eq!(ch(r"'\x80\x00'"), 0x8000);
732
733        assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
734        assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
735        assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
736        assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
737        assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
738    }
739
740    /// A wide constant has room for exactly one character, so two is already too many and the
741    /// last one is what survives. `u8` is the one encoding where GCC makes this an error.
742    #[test]
743    fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
744        for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
745            let out = character(text, Std::C23, &linux()).expect("a constant");
746            assert_eq!(out.value, 0x62, "{text}");
747            assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
748        }
749        assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
750        assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
751    }
752
753    #[test]
754    fn the_empty_constant_has_no_value_to_have() {
755        assert_eq!(ch_error("''"), LiteralError::Empty);
756        assert_eq!(ch_error("L''"), LiteralError::Empty);
757        // The empty string is fine, and is one element long once the terminator is there.
758        assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
759        assert_eq!(str_bytes(r#""""#), vec![0]);
760    }
761
762    /// A character from the source is encoded in the literal's encoding, so the same `é` is
763    /// two bytes in one constant and one code point in another.
764    #[test]
765    fn a_source_character_is_encoded_and_an_escape_is_not() {
766        assert_eq!(ch("'é'"), 0xc3a9);
767        assert_eq!(ch("L'é'"), 0xe9);
768        assert_eq!(ch("u'€'"), 0x20ac);
769        assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
770        // The same character in a plain constant is its UTF-8 bytes, which makes it a
771        // multi-character constant and a negative number.
772        assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
773        assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
774    }
775
776    /// Both compilers have `\e` and neither standard does, and an escape that means nothing is
777    /// the letter itself after a warning rather than an error.
778    #[test]
779    fn the_escapes_outside_the_standard_still_have_values() {
780        assert_eq!(ch(r"'\e'"), 0x1b);
781        assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
782        assert_eq!(ch(r"'\q'"), 0x71);
783        assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
784        assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
785        assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
786    }
787
788    /// A universal character name may not name a character in the basic character set, which
789    /// GCC still enforces in C23 where the wording was relaxed, and the three characters below
790    /// a space that are allowed anyway are allowed here too.
791    #[test]
792    fn a_universal_character_name_may_not_name_just_anything() {
793        assert_eq!(ch("'\\u0024'"), 0x24);
794        assert_eq!(ch("'\\u00e9'"), 0xc3a9);
795        assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
796        assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
797        assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
798        // GCC warns here and encodes the value anyway. clang refuses it and so does this.
799        assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
800    }
801
802    #[test]
803    fn a_universal_character_name_before_c99_is_worth_a_remark() {
804        let out = character("'\\u00e9'", Std::C89, &linux()).expect("a constant");
805        assert!(out.remarks.has(Remarks::UCN));
806        let out = character("'\\u00e9'", Std::C99, &linux()).expect("a constant");
807        assert!(!out.remarks.has(Remarks::UCN));
808    }
809
810    /// Octal runs to three digits and stops, and hexadecimal runs as far as the digits go, so
811    /// `"\1234"` is two characters and `"\x41z"` is two as well.
812    #[test]
813    fn an_octal_escape_ends_and_a_hex_escape_does_not() {
814        assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
815        assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
816        assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
817    }
818
819    /// The sizes GCC reports for these, which is the elements plus the terminator times the
820    /// width of one.
821    #[test]
822    fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
823        assert_eq!(str_bytes(r#""abc""#).len(), 4);
824        assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
825        assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
826        assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
827        assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
828        // A zero in the middle is an element like any other, and the terminator is still added.
829        assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
830        assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
831    }
832
833    /// The one encoding where a character can take two elements, which is why a wide string is
834    /// not the same length on Windows as it is anywhere else.
835    #[test]
836    fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
837        assert_eq!(
838            str_elements(r#"u8"é€😀""#),
839            vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
840        );
841        assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
842        assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
843    }
844
845    /// A wide literal is UTF-16 on Windows and UTF-32 everywhere else, so the same three
846    /// characters are four elements on one target and three on the other.
847    #[test]
848    fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
849        let text = r#"L"a😀""#;
850        let here = string(text, Std::C23, &linux()).expect("a string");
851        assert_eq!(here.elements, vec![0x61, 0x1f600]);
852        assert_eq!(here.bytes(&linux()).len(), 12);
853        let there = string(text, Std::C23, &windows()).expect("a string");
854        assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
855        assert_eq!(there.bytes(&windows()).len(), 8);
856        // And a wide character constant takes the sign of `wchar_t`, which is not the same on
857        // every target either.
858        assert_eq!(character(r"L'\xffffffff'", Std::C23, &linux()).expect("a constant").value, -1);
859        assert_eq!(
860            character(r"L'\xffffffff'", Std::C23, &arm()).expect("a constant").value,
861            0xffff_ffff
862        );
863    }
864
865    /// Every target the compiler has is little-endian, so the other order is checked by
866    /// flipping the field rather than by naming a target, and this is the test that fails on
867    /// the day a big-endian one arrives with the layout still assuming otherwise.
868    #[test]
869    fn the_bytes_come_out_in_the_targets_order() {
870        let mut big = linux();
871        big.little_endian = false;
872        let literal = string(r#"u"ab""#, Std::C23, &big).expect("a string");
873        assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
874        assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
875    }
876
877    /// `L` is C89, `u` and `U` are C11, and `u8` is C11 on a string and C23 on a character
878    /// constant, which is the one place the two differ.
879    #[test]
880    fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
881        assert!(character("L'a'", Std::C89, &linux()).is_ok());
882        assert_eq!(
883            character("u'a'", Std::C99, &linux()).expect_err("not in C99"),
884            LiteralError::PrefixNotInDialect
885        );
886        assert!(character("u'a'", Std::C11, &linux()).is_ok());
887        assert!(string(r#"u8"a""#, Std::C11, &linux()).is_ok());
888        assert_eq!(
889            character("u8'a'", Std::C11, &linux()).expect_err("not in C11"),
890            LiteralError::PrefixNotInDialect
891        );
892        assert!(character("u8'a'", Std::C23, &linux()).is_ok());
893    }
894
895    /// The widths and signs the elements have, which is what the parser will turn into the
896    /// type of the literal.
897    #[test]
898    fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
899        let target = linux();
900        assert_eq!(Encoding::Plain.element_width(&target), 8);
901        assert_eq!(Encoding::Utf8.element_width(&target), 8);
902        assert_eq!(Encoding::Utf16.element_width(&target), 16);
903        assert_eq!(Encoding::Utf32.element_width(&target), 32);
904        assert_eq!(Encoding::Wide.element_width(&target), 32);
905        assert_eq!(Encoding::Wide.element_width(&windows()), 16);
906
907        assert!(Encoding::Plain.is_signed(&target));
908        assert!(!Encoding::Plain.is_signed(&arm()));
909        assert!(Encoding::Wide.is_signed(&target));
910        assert!(!Encoding::Wide.is_signed(&arm()));
911        assert!(!Encoding::Utf8.is_signed(&target));
912        assert!(!Encoding::Utf16.is_signed(&target));
913        assert!(!Encoding::Utf32.is_signed(&target));
914    }
915
916    #[test]
917    fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
918        assert_eq!(ch_error("a"), LiteralError::NotALiteral);
919        assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
920        assert_eq!(
921            string("'a'", Std::C23, &linux()).expect_err("not a string"),
922            LiteralError::NotALiteral
923        );
924        assert_eq!(ch_error("'"), LiteralError::NotALiteral);
925    }
926
927    #[test]
928    fn every_error_has_something_to_print() {
929        for error in [
930            LiteralError::NotALiteral,
931            LiteralError::Empty,
932            LiteralError::TooLong,
933            LiteralError::NoHexDigits,
934            LiteralError::IncompleteUcn,
935            LiteralError::InvalidUcn,
936            LiteralError::NamedUcn,
937            LiteralError::InvalidUtf8,
938            LiteralError::PrefixNotInDialect,
939            LiteralError::MixedEncodings,
940        ] {
941            assert!(!error.message().is_empty());
942        }
943    }
944}