Skip to content

Commit d6736a1

Browse files
committed
auto merge of #14880 : SimonSapin/rust/byte-literals, r=alexcrichton
See #14646 (tracking issue) and rust-lang/rfcs#69. This does not close the tracking issue, as the `bytes!()` macro still needs to be removed. It will be later, after a snapshot is made with the changes in this PR, so that the new syntax can be used when bootstrapping the compiler.
2 parents 5c81a18 + 3744d82 commit d6736a1

File tree

24 files changed

+469
-108
lines changed

24 files changed

+469
-108
lines changed

src/doc/rust.md

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ rule. A literal is a form of constant expression, so is evaluated (primarily)
234234
at compile time.
235235

236236
~~~~ {.ebnf .gram}
237-
literal : string_lit | char_lit | num_lit ;
237+
literal : string_lit | char_lit | byte_string_lit | byte_lit | num_lit ;
238238
~~~~
239239

240240
#### Character and string literals
@@ -244,17 +244,17 @@ char_lit : '\x27' char_body '\x27' ;
244244
string_lit : '"' string_body * '"' | 'r' raw_string ;
245245
246246
char_body : non_single_quote
247-
| '\x5c' [ '\x27' | common_escape ] ;
247+
| '\x5c' [ '\x27' | common_escape | unicode_escape ] ;
248248
249249
string_body : non_double_quote
250-
| '\x5c' [ '\x22' | common_escape ] ;
250+
| '\x5c' [ '\x22' | common_escape | unicode_escape ] ;
251251
raw_string : '"' raw_string_body '"' | '#' raw_string '#' ;
252252
253253
common_escape : '\x5c'
254254
| 'n' | 'r' | 't' | '0'
255255
| 'x' hex_digit 2
256-
| 'u' hex_digit 4
257-
| 'U' hex_digit 8 ;
256+
unicode_escape : 'u' hex_digit 4
257+
| 'U' hex_digit 8 ;
258258
259259
hex_digit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
260260
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
@@ -294,7 +294,7 @@ the following forms:
294294
escaped in order to denote *itself*.
295295

296296
Raw string literals do not process any escapes. They start with the character
297-
`U+0072` (`r`), followed zero or more of the character `U+0023` (`#`) and a
297+
`U+0072` (`r`), followed by zero or more of the character `U+0023` (`#`) and a
298298
`U+0022` (double-quote) character. The _raw string body_ is not defined in the
299299
EBNF grammar above: it can contain any sequence of Unicode characters and is
300300
terminated only by another `U+0022` (double-quote) character, followed by the
@@ -319,6 +319,65 @@ r##"foo #"# bar"##; // foo #"# bar
319319
"\\x52"; r"\x52"; // \x52
320320
~~~~
321321

322+
#### Byte and byte string literals
323+
324+
~~~~ {.ebnf .gram}
325+
byte_lit : 'b' '\x27' byte_body '\x27' ;
326+
byte_string_lit : 'b' '"' string_body * '"' | 'b' 'r' raw_byte_string ;
327+
328+
byte_body : ascii_non_single_quote
329+
| '\x5c' [ '\x27' | common_escape ] ;
330+
331+
byte_string_body : ascii_non_double_quote
332+
| '\x5c' [ '\x22' | common_escape ] ;
333+
raw_byte_string : '"' raw_byte_string_body '"' | '#' raw_byte_string '#' ;
334+
335+
~~~~
336+
337+
A _byte literal_ is a single ASCII character (in the `U+0000` to `U+007F` range)
338+
enclosed within two `U+0027` (single-quote) characters,
339+
with the exception of `U+0027` itself,
340+
which must be _escaped_ by a preceding U+005C character (`\`),
341+
or a single _escape_.
342+
It is equivalent to a `u8` unsigned 8-bit integer _number literal_.
343+
344+
A _byte string literal_ is a sequence of ASCII characters and _escapes_
345+
enclosed within two `U+0022` (double-quote) characters,
346+
with the exception of `U+0022` itself,
347+
which must be _escaped_ by a preceding `U+005C` character (`\`),
348+
or a _raw byte string literal_.
349+
It is equivalent to a `&'static [u8]` borrowed vectior unsigned 8-bit integers.
350+
351+
Some additional _escapes_ are available in either byte or non-raw byte string
352+
literals. An escape starts with a `U+005C` (`\`) and continues with one of
353+
the following forms:
354+
355+
* An _byte escape_ escape starts with `U+0078` (`x`) and is
356+
followed by exactly two _hex digits_. It denotes the byte
357+
equal to the provided hex value.
358+
* A _whitespace escape_ is one of the characters `U+006E` (`n`), `U+0072`
359+
(`r`), or `U+0074` (`t`), denoting the bytes values `0x0A` (ASCII LF),
360+
`0x0D` (ASCII CR) or `0x09` (ASCII HT) respectively.
361+
* The _backslash escape_ is the character `U+005C` (`\`) which must be
362+
escaped in order to denote its ASCII encoding `0x5C`.
363+
364+
Raw byte string literals do not process any escapes.
365+
They start with the character `U+0072` (`r`),
366+
followed by `U+0062` (`b`),
367+
followed by zero or more of the character `U+0023` (`#`),
368+
and a `U+0022` (double-quote) character.
369+
The _raw string body_ is not defined in the EBNF grammar above:
370+
it can contain any sequence of ASCII characters and is
371+
terminated only by another `U+0022` (double-quote) character, followed by the
372+
same number of `U+0023` (`#`) characters that preceded the opening `U+0022`
373+
(double-quote) character.
374+
A raw byte string literal can not contain any non-ASCII byte.
375+
376+
All characters contained in the raw string body represent their ASCII encoding,
377+
the characters `U+0022` (double-quote) (except when followed by at least as
378+
many `U+0023` (`#`) characters as were used to start the raw string literal) or
379+
`U+005C` (`\`) do not have any special meaning.
380+
322381
#### Number literals
323382

324383
~~~~ {.ebnf .gram}

src/libcore/str.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ Section: Comparing strings
560560

561561
// share the implementation of the lang-item vs. non-lang-item
562562
// eq_slice.
563+
/// NOTE: This function is (ab)used in rustc::middle::trans::_match
564+
/// to compare &[u8] byte slices that are not necessarily valid UTF-8.
563565
#[inline]
564566
fn eq_slice_(a: &str, b: &str) -> bool {
565567
#[allow(ctypes)]
@@ -572,6 +574,8 @@ fn eq_slice_(a: &str, b: &str) -> bool {
572574
}
573575

574576
/// Bytewise slice equality
577+
/// NOTE: This function is (ab)used in rustc::middle::trans::_match
578+
/// to compare &[u8] byte slices that are not necessarily valid UTF-8.
575579
#[cfg(not(test))]
576580
#[lang="str_eq"]
577581
#[inline]

src/libregex_macros/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ fn exec<'t>(which: ::regex::native::MatchKind, input: &'t str,
182182
#[allow(unused_variable)]
183183
fn run(&mut self, start: uint, end: uint) -> Vec<Option<uint>> {
184184
let mut matched = false;
185-
let prefix_bytes: &[u8] = &$prefix_bytes;
185+
let prefix_bytes: &[u8] = $prefix_bytes;
186186
let mut clist = &mut Threads::new(self.which);
187187
let mut nlist = &mut Threads::new(self.which);
188188

src/librustc/middle/const_eval.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ pub fn lit_to_const(lit: &Lit) -> const_val {
506506
LitBinary(ref data) => {
507507
const_binary(Rc::new(data.iter().map(|x| *x).collect()))
508508
}
509+
LitByte(n) => const_uint(n as u64),
509510
LitChar(n) => const_uint(n as u64),
510511
LitInt(n, _) => const_int(n),
511512
LitUint(n, _) => const_uint(n),
@@ -528,6 +529,7 @@ pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<int> {
528529
(&const_float(a), &const_float(b)) => compare_vals(a, b),
529530
(&const_str(ref a), &const_str(ref b)) => compare_vals(a, b),
530531
(&const_bool(a), &const_bool(b)) => compare_vals(a, b),
532+
(&const_binary(ref a), &const_binary(ref b)) => compare_vals(a, b),
531533
_ => None
532534
}
533535
}

src/librustc/middle/lint.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ fn check_type_limits(cx: &Context, e: &ast::Expr) {
805805
} else { t };
806806
let (min, max) = uint_ty_range(uint_type);
807807
let lit_val: u64 = match lit.node {
808+
ast::LitByte(_v) => return, // _v is u8, within range by definition
808809
ast::LitInt(v, _) => v as u64,
809810
ast::LitUint(v, _) => v,
810811
ast::LitIntUnsuffixed(v) => v as u64,

src/librustc/middle/trans/_match.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,13 +1273,24 @@ fn compare_values<'a>(
12731273
val: bool_to_i1(result.bcx, result.val)
12741274
}
12751275
}
1276-
_ => cx.sess().bug("only scalars and strings supported in compare_values"),
1276+
_ => cx.sess().bug("only strings supported in compare_values"),
12771277
},
12781278
ty::ty_rptr(_, mt) => match ty::get(mt.ty).sty {
12791279
ty::ty_str => compare_str(cx, lhs, rhs, rhs_t),
1280-
_ => cx.sess().bug("only scalars and strings supported in compare_values"),
1280+
ty::ty_vec(mt, _) => match ty::get(mt.ty).sty {
1281+
ty::ty_uint(ast::TyU8) => {
1282+
// NOTE: cast &[u8] to &str and abuse the str_eq lang item,
1283+
// which calls memcmp().
1284+
let t = ty::mk_str_slice(cx.tcx(), ty::ReStatic, ast::MutImmutable);
1285+
let lhs = BitCast(cx, lhs, type_of::type_of(cx.ccx(), t).ptr_to());
1286+
let rhs = BitCast(cx, rhs, type_of::type_of(cx.ccx(), t).ptr_to());
1287+
compare_str(cx, lhs, rhs, rhs_t)
1288+
},
1289+
_ => cx.sess().bug("only byte strings supported in compare_values"),
1290+
},
1291+
_ => cx.sess().bug("on string and byte strings supported in compare_values"),
12811292
},
1282-
_ => cx.sess().bug("only scalars and strings supported in compare_values"),
1293+
_ => cx.sess().bug("only scalars, byte strings, and strings supported in compare_values"),
12831294
}
12841295
}
12851296

src/librustc/middle/trans/consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: ast::Lit)
4343
-> ValueRef {
4444
let _icx = push_ctxt("trans_lit");
4545
match lit.node {
46+
ast::LitByte(b) => C_integral(Type::uint_from_ty(cx, ast::TyU8), b as u64, false),
4647
ast::LitChar(i) => C_integral(Type::char(cx), i as u64, false),
4748
ast::LitInt(i, t) => C_integral(Type::int_from_ty(cx, t), i as u64, true),
4849
ast::LitUint(u, t) => C_integral(Type::uint_from_ty(cx, t), u, false),

src/librustc/middle/typeck/check/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,6 +1715,7 @@ pub fn check_lit(fcx: &FnCtxt, lit: &ast::Lit) -> ty::t {
17151715
ast::LitBinary(..) => {
17161716
ty::mk_slice(tcx, ty::ReStatic, ty::mt{ ty: ty::mk_u8(), mutbl: ast::MutImmutable })
17171717
}
1718+
ast::LitByte(_) => ty::mk_u8(),
17181719
ast::LitChar(_) => ty::mk_char(),
17191720
ast::LitInt(_, t) => ty::mk_mach_int(t),
17201721
ast::LitUint(_, t) => ty::mk_mach_uint(t),

src/librustdoc/clean/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,6 +1924,14 @@ fn lit_to_str(lit: &ast::Lit) -> String {
19241924
match lit.node {
19251925
ast::LitStr(ref st, _) => st.get().to_string(),
19261926
ast::LitBinary(ref data) => format!("{:?}", data.as_slice()),
1927+
ast::LitByte(b) => {
1928+
let mut res = String::from_str("b'");
1929+
(b as char).escape_default(|c| {
1930+
res.push_char(c);
1931+
});
1932+
res.push_char('\'');
1933+
res
1934+
},
19271935
ast::LitChar(c) => format!("'{}'", c),
19281936
ast::LitInt(i, _t) => i.to_str(),
19291937
ast::LitUint(u, _t) => u.to_str(),

src/librustdoc/html/highlight.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader,
140140
}
141141

142142
// text literals
143-
t::LIT_CHAR(..) | t::LIT_STR(..) | t::LIT_STR_RAW(..) => "string",
143+
t::LIT_BYTE(..) | t::LIT_BINARY(..) | t::LIT_BINARY_RAW(..) |
144+
t::LIT_CHAR(..) | t::LIT_STR(..) | t::LIT_STR_RAW(..) => "string",
144145

145146
// number literals
146147
t::LIT_INT(..) | t::LIT_UINT(..) | t::LIT_INT_UNSUFFIXED(..) |

src/libsyntax/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@ pub type Lit = Spanned<Lit_>;
616616
pub enum Lit_ {
617617
LitStr(InternedString, StrStyle),
618618
LitBinary(Rc<Vec<u8> >),
619+
LitByte(u8),
619620
LitChar(char),
620621
LitInt(i64, IntTy),
621622
LitUint(u64, UintTy),

src/libsyntax/ext/concat.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
4747
ast::LitBool(b) => {
4848
accumulator.push_str(format!("{}", b).as_slice());
4949
}
50+
ast::LitByte(..) |
5051
ast::LitBinary(..) => {
5152
cx.span_err(e.span, "cannot concatenate a binary literal");
5253
}

src/libsyntax/ext/quote.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,12 @@ fn mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> Gc<ast::Expr> {
436436
vec!(mk_binop(cx, sp, binop)));
437437
}
438438

439+
LIT_BYTE(i) => {
440+
let e_byte = cx.expr_lit(sp, ast::LitByte(i));
441+
442+
return cx.expr_call(sp, mk_token_path(cx, sp, "LIT_BYTE"), vec!(e_byte));
443+
}
444+
439445
LIT_CHAR(i) => {
440446
let e_char = cx.expr_lit(sp, ast::LitChar(i));
441447

0 commit comments

Comments
 (0)