Current behaviour is similar to normal string literals, except they are type-checked
as slice types instead of array, and the compiled fat pointer has +1 to size to
include the null terminator.
gcc/rust/ChangeLog:
* lex/rust-token.h (RS_TOKEN_LIST): Add C_STRING_LITERAL and RAW_C_STRING_LITERAL
(unused for now).
* lex/rust-lex.h (Lexer): Define new parse_c_string function.
* lex/rust-lex.cc (Lexer::build_token): Implement lexing for C-style string
literals.
(Lexer::parse_c_string): Add implementation.
* hir/tree/rust-hir-literal.h (HIR::Literal): Define new C_STRING LitType.
* hir/rust-ast-lower-base.cc (ASTLoweringBase::lower_literal): Add new case for
C_STRING.
* parse/rust-parse-impl-attribute.hxx (Parser<ManagedTokenSource>::parse_attr_input):
Add new case for C_STRING.
* parse/rust-parse-impl-expr.hxx (Parser<ManagedTokenSource>::parse_literal_expr):
Add new case for parsing C_STRING.
(Parser<ManagedTokenSource>::null_denotation_not_path): Add new case for C_STRING.
* ast/rust-ast.h (Token::is_string_lit): Add new case for C_STRING_LITERAL.
* ast/rust-ast.cc (AttributeParser::parse_meta_item_inner): Add new case for
C_STRING_LITERAL.
* ast/rust-ast-collector.cc (TokenCollector::visit): Add new case for
C_STRING_LITERAL.
* typecheck/rust-hir-type-check-base.cc (TypeCheckBase::resolve_literal):
Implement type checking for the new C_STRING type.
* backend/rust-compile-expr.h (CompileExpr): Define new function
compile_c_string_literal.
* backend/rust-compile-expr.cc (CompileExpr::visit(LiteralExpr)): Add new case
for C_STRING.
(CompileExpr::compile_c_string_literal): Implement compilation of C-style string
literals.
Signed-off-by: Yap Zhi Heng <yapzhhg@gmail.com>
case RAW_STRING_LITERAL:
push (Rust::Token::make_raw_string (tok.get_locus (), std::move (data)));
break;
+ case C_STRING_LITERAL:
+ push (Rust::Token::make_c_string (tok.get_locus (), std::move (data)));
+ break;
case INNER_DOC_COMMENT:
push (Rust::Token::make_inner_doc_comment (tok.get_locus (),
std::move (data)));
case Literal::LitType::RAW_STRING:
push (Rust::Token::make_raw_string (locus, std::move (value)));
break;
+ case Literal::LitType::C_STRING:
+ push (Rust::Token::make_c_string (locus, std::move (value)));
+ break;
case Literal::LitType::INT:
{
auto val_len = value.length ();
case BYTE_CHAR_LITERAL:
case BYTE_STRING_LITERAL:
case RAW_STRING_LITERAL:
+ case C_STRING_LITERAL:
case INT_LITERAL:
case FLOAT_LITERAL:
case TRUE_LITERAL:
case STRING_LITERAL:
case BYTE_STRING_LITERAL:
case RAW_STRING_LITERAL:
+ case C_STRING_LITERAL:
return true;
default:
return false;
BYTE,
BYTE_STRING,
RAW_STRING,
+ C_STRING,
INT,
FLOAT,
BOOL,
case HIR::Literal::BYTE_STRING:
translated = compile_byte_string_literal (expr, tyty);
return;
+
+ case HIR::Literal::C_STRING:
+ translated = compile_c_string_literal (expr, tyty);
+ return;
}
}
return address_expression (constructed, expr.get_locus ());
}
+tree
+CompileExpr::compile_c_string_literal (const HIR::LiteralExpr &expr,
+ const TyTy::BaseType *tyty)
+{
+ // Copied from compile_string_literal
+ tree fat_pointer = TyTyResolveCompile::compile (ctx, tyty);
+
+ rust_assert (expr.get_lit_type () == HIR::Literal::C_STRING);
+ const auto literal_value = expr.get_literal ();
+
+ auto base = Backend::string_constant_expression (literal_value.as_string ());
+ tree data = address_expression (base, expr.get_locus ());
+
+ TyTy::BaseType *usize = nullptr;
+ bool ok = ctx->get_tyctx ()->lookup_builtin ("usize", &usize);
+ rust_assert (ok);
+ tree type = TyTyResolveCompile::compile (ctx, usize);
+
+ // +1 for null terminator, unlike Rust string literals.
+ tree size = build_int_cstu (type, literal_value.as_string ().size () + 1);
+
+ return Backend::constructor_expression (fat_pointer, false, {data, size}, -1,
+ expr.get_locus ());
+}
+
tree
CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree,
location_t location)
tree compile_byte_string_literal (const HIR::LiteralExpr &expr,
const TyTy::BaseType *tyty);
+ tree compile_c_string_literal (const HIR::LiteralExpr &expr,
+ const TyTy::BaseType *tyty);
+
tree type_cast_expression (tree type_to_cast_to, tree expr, location_t locus);
tree array_value_expr (location_t expr_locus,
case AST::Literal::LitType::RAW_STRING:
type = HIR::Literal::LitType::STRING;
break;
+ case AST::Literal::LitType::C_STRING:
+ type = HIR::Literal::LitType::C_STRING;
+ break;
case AST::Literal::LitType::INT:
type = HIR::Literal::LitType::INT;
break;
STRING,
BYTE,
BYTE_STRING,
+ C_STRING,
INT,
FLOAT,
BOOL
return parse_raw_byte_string (loc);
}
+ // C-style strings
+ else if (current_char == 'c' && peek_input () == '"')
+ return parse_c_string (loc);
+
// raw identifiers and raw strings
if (current_char == 'r')
{
return Token::make_byte_string (loc, std::move (str));
}
+// Parses a C-style string.
+TokenPtr
+Lexer::parse_c_string (location_t loc)
+{
+ skip_input ();
+ current_column++;
+
+ // Mostly same code copied from parse_string...
+
+ std::string str;
+ str.reserve (16); // some sensible default
+
+ current_char = peek_input ();
+
+ const location_t string_begin_locus = get_current_location ();
+
+ while (current_char.value != '"' && !current_char.is_eof ())
+ {
+ if (current_char.value == '\\')
+ {
+ int length = 1;
+
+ auto escape_pair = parse_escape ('"');
+ current_char = std::get<0> (escape_pair);
+
+ if (current_char == Codepoint (0) && std::get<2> (escape_pair))
+ length = std::get<1> (escape_pair) - 1;
+ else
+ length += std::get<1> (escape_pair);
+
+ if (current_char != Codepoint (0) || !std::get<2> (escape_pair))
+ str += current_char.as_string ();
+
+ current_column += length;
+
+ // FIXME: parse_escape does not update current_char correctly.
+ current_char = peek_input ();
+ continue;
+ }
+
+ current_column++;
+ if (current_char.value == '\n')
+ {
+ current_line++;
+ current_column = 1;
+ // tell line_table that new line starts
+ start_line (current_line, max_column_hint);
+ }
+
+ str += current_char;
+ skip_input ();
+ current_char = peek_input ();
+ }
+
+ if (current_char.value == '"')
+ {
+ current_column++;
+
+ skip_input ();
+ current_char = peek_input ();
+ }
+ else if (current_char.is_eof ())
+ {
+ rust_error_at (string_begin_locus, "unended C string literal");
+ return Token::make (END_OF_FILE, get_current_location ());
+ }
+ else
+ {
+ rust_unreachable ();
+ }
+
+ str.shrink_to_fit ();
+
+ return Token::make_c_string (loc, std::move (str));
+}
+
// Parses a raw byte string.
TokenPtr
Lexer::parse_raw_byte_string (location_t loc)
TokenPtr parse_string (location_t loc);
TokenPtr maybe_parse_raw_string (location_t loc);
TokenPtr parse_raw_string (location_t loc, int initial_hash_count);
+ TokenPtr parse_c_string (location_t loc);
TokenPtr parse_non_decimal_int_literals (location_t loc);
TokenPtr parse_decimal_int_or_float (location_t loc);
TokenPtr parse_char_or_lifetime (location_t loc);
RS_TOKEN (BYTE_STRING_LITERAL, "byte string literal") \
RS_TOKEN (RAW_STRING_LITERAL, "raw string literal") \
RS_TOKEN (BYTE_CHAR_LITERAL, "byte character literal") \
+ RS_TOKEN (C_STRING_LITERAL, "C string literal") \
+ RS_TOKEN (RAW_C_STRING_LITERAL, "raw C string literal") \
RS_TOKEN (LIFETIME, "lifetime") /* TODO: improve token type */ \
/* Have "interpolated" tokens (whatever that means)? identifer, path, type, \
* pattern, */ \
return TokenPtr (new Token (RAW_STRING_LITERAL, locus, std::move (str)));
}
+ static TokenPtr make_c_string (location_t locus, std::string str)
+ {
+ return TokenPtr (new Token (C_STRING_LITERAL, locus, std::move (str)));
+ }
+
// Makes and returns a new TokenPtr of type INNER_DOC_COMMENT.
static TokenPtr make_inner_doc_comment (location_t locus, std::string str)
{
case BYTE_CHAR_LITERAL:
case BYTE_STRING_LITERAL:
case RAW_STRING_LITERAL:
+ case C_STRING_LITERAL:
return true;
default:
return false;
case BYTE_STRING_LITERAL:
lit_type = AST::Literal::BYTE_STRING;
break;
+ case C_STRING_LITERAL:
+ lit_type = AST::Literal::C_STRING;
+ break;
case RAW_STRING_LITERAL:
lit_type = AST::Literal::RAW_STRING;
break;
literal_value = t->get_str ();
lexer.skip_token ();
break;
+ case C_STRING_LITERAL:
+ type = AST::Literal::C_STRING;
+ literal_value = t->get_str ();
+ lexer.skip_token ();
+ break;
case INT_LITERAL:
type = AST::Literal::INT;
literal_value = LiteralResolve::evaluate_integer_literal (t);
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::RAW_STRING,
tok->get_type_hint (), {}, tok->get_locus ()));
+ case C_STRING_LITERAL:
+ return std::unique_ptr<AST::LiteralExpr> (
+ new AST::LiteralExpr (tok->get_str (), AST::Literal::C_STRING,
+ tok->get_type_hint (), {}, tok->get_locus ()));
case CHAR_LITERAL:
return std::unique_ptr<AST::LiteralExpr> (
new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR,
TyTy::Region::make_static ());
}
break;
+ case HIR::Literal::LitType::C_STRING:
+ {
+ // Throw error if C string literal contains null byte
+ if (literal.as_string ().find ('\0') != std::string::npos)
+ {
+ rust_error_at (
+ locus, "null characters in C string literals are not supported");
+ infered = new TyTy::ErrorType (expr_mappings.get_hirid (), locus);
+ break;
+ }
+
+ /* This is a pointer to a null-terminated byte slice (&[u8]). */
+ TyTy::BaseType *u8;
+ auto ok = context->lookup_builtin ("u8", &u8);
+ rust_assert (ok);
+ auto crate_num = mappings.get_current_crate ();
+ Analysis::NodeMapping slice_mapping (crate_num, UNKNOWN_NODEID,
+ mappings.get_next_hir_id (
+ crate_num),
+ UNKNOWN_LOCAL_DEFID);
+
+ TyTy::SliceType *slice
+ = new TyTy::SliceType (slice_mapping.get_hirid (), locus,
+ TyTy::TyVar (u8->get_ref ()));
+ context->insert_type (slice_mapping, slice);
+
+ infered = new TyTy::ReferenceType (expr_mappings.get_hirid (),
+ TyTy::TyVar (slice->get_ref ()),
+ Mutability::Imm,
+ TyTy::Region::make_static ());
+ }
+ break;
default:
rust_unreachable ();
break;
--- /dev/null
+#![feature(no_core)]
+#![no_core]
+
+pub fn main() {
+ let _fail = c"gc\0crs";
+ // { dg-error "null characters in C string literals are not supported" "" { target *-*-* } .-1 }
+}
\ No newline at end of file
--- /dev/null
+// { dg-output "gccrs\n" }
+#![feature(no_core)]
+#![no_core]
+
+extern "C" {
+ fn printf(s: *const i8, ...);
+}
+
+pub fn main() {
+ let a = c"gccrs";
+ unsafe {
+ printf(a as *const [u8] as *const i8); // TODO change *const [u8] to .as_ptr() when C strings are compiled to their own CStr type
+ }
+}