From: Yap Zhi Heng <[email protected]>
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 <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/ac0e6b2c8c8dc3b1f9a47d4a4e2663422fd4d719
The commit has NOT been mentioned in any issue.
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4565
gcc/rust/ast/rust-ast-collector.cc | 6 ++
gcc/rust/ast/rust-ast.cc | 1 +
gcc/rust/ast/rust-ast.h | 2 +
gcc/rust/backend/rust-compile-expr.cc | 29 +++++++
gcc/rust/backend/rust-compile-expr.h | 3 +
gcc/rust/hir/rust-ast-lower-base.cc | 3 +
gcc/rust/hir/tree/rust-hir-literal.h | 1 +
gcc/rust/lex/rust-lex.cc | 80 +++++++++++++++++++
gcc/rust/lex/rust-lex.h | 1 +
gcc/rust/lex/rust-token.h | 8 ++
gcc/rust/parse/rust-parse-impl-attribute.hxx | 3 +
gcc/rust/parse/rust-parse-impl-expr.hxx | 9 +++
.../typecheck/rust-hir-type-check-base.cc | 32 ++++++++
.../rust/compile/c_string_null_byte_check.rs | 7 ++
.../rust/execute/torture/c_string.rs | 14 ++++
15 files changed, 199 insertions(+)
create mode 100644 gcc/testsuite/rust/compile/c_string_null_byte_check.rs
create mode 100644 gcc/testsuite/rust/execute/torture/c_string.rs
diff --git a/gcc/rust/ast/rust-ast-collector.cc
b/gcc/rust/ast/rust-ast-collector.cc
index 60be5e447..f0d8957ef 100644
--- a/gcc/rust/ast/rust-ast-collector.cc
+++ b/gcc/rust/ast/rust-ast-collector.cc
@@ -429,6 +429,9 @@ TokenCollector::visit (Token &tok)
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)));
@@ -866,6 +869,9 @@ TokenCollector::visit (Literal &lit, location_t locus)
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 ();
diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc
index 5880ea92f..872dff315 100644
--- a/gcc/rust/ast/rust-ast.cc
+++ b/gcc/rust/ast/rust-ast.cc
@@ -3620,6 +3620,7 @@ AttributeParser::parse_meta_item_inner ()
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:
diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h
index f6b704507..8afa04beb 100644
--- a/gcc/rust/ast/rust-ast.h
+++ b/gcc/rust/ast/rust-ast.h
@@ -209,6 +209,7 @@ public:
case STRING_LITERAL:
case BYTE_STRING_LITERAL:
case RAW_STRING_LITERAL:
+ case C_STRING_LITERAL:
return true;
default:
return false;
@@ -272,6 +273,7 @@ public:
BYTE,
BYTE_STRING,
RAW_STRING,
+ C_STRING,
INT,
FLOAT,
BOOL,
diff --git a/gcc/rust/backend/rust-compile-expr.cc
b/gcc/rust/backend/rust-compile-expr.cc
index 7529744d5..45765e72d 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -1089,6 +1089,10 @@ CompileExpr::visit (HIR::LiteralExpr &expr)
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;
}
}
@@ -1902,6 +1906,31 @@ CompileExpr::compile_byte_string_literal (const
HIR::LiteralExpr &expr,
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)
diff --git a/gcc/rust/backend/rust-compile-expr.h
b/gcc/rust/backend/rust-compile-expr.h
index 63ada9f33..532e140c9 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -131,6 +131,9 @@ protected:
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,
diff --git a/gcc/rust/hir/rust-ast-lower-base.cc
b/gcc/rust/hir/rust-ast-lower-base.cc
index 6fc4463a9..a1f84c9f0 100644
--- a/gcc/rust/hir/rust-ast-lower-base.cc
+++ b/gcc/rust/hir/rust-ast-lower-base.cc
@@ -1018,6 +1018,9 @@ ASTLoweringBase::lower_literal (const AST::Literal
&literal)
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;
diff --git a/gcc/rust/hir/tree/rust-hir-literal.h
b/gcc/rust/hir/tree/rust-hir-literal.h
index b007f800f..7b0303401 100644
--- a/gcc/rust/hir/tree/rust-hir-literal.h
+++ b/gcc/rust/hir/tree/rust-hir-literal.h
@@ -33,6 +33,7 @@ public:
STRING,
BYTE,
BYTE_STRING,
+ C_STRING,
INT,
FLOAT,
BOOL
diff --git a/gcc/rust/lex/rust-lex.cc b/gcc/rust/lex/rust-lex.cc
index 4f135b19a..f5f2f4aa6 100644
--- a/gcc/rust/lex/rust-lex.cc
+++ b/gcc/rust/lex/rust-lex.cc
@@ -1063,6 +1063,10 @@ Lexer::build_token ()
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')
{
@@ -1749,6 +1753,82 @@ Lexer::parse_byte_string (location_t loc)
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)
diff --git a/gcc/rust/lex/rust-lex.h b/gcc/rust/lex/rust-lex.h
index 132005a16..8d65a6ddf 100644
--- a/gcc/rust/lex/rust-lex.h
+++ b/gcc/rust/lex/rust-lex.h
@@ -147,6 +147,7 @@ private:
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);
diff --git a/gcc/rust/lex/rust-token.h b/gcc/rust/lex/rust-token.h
index f3e2e9441..670affd72 100644
--- a/gcc/rust/lex/rust-token.h
+++ b/gcc/rust/lex/rust-token.h
@@ -134,6 +134,8 @@ enum PrimitiveCoreType
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, */
\
@@ -422,6 +424,11 @@ public:
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)
{
@@ -504,6 +511,7 @@ public:
case BYTE_CHAR_LITERAL:
case BYTE_STRING_LITERAL:
case RAW_STRING_LITERAL:
+ case C_STRING_LITERAL:
return true;
default:
return false;
diff --git a/gcc/rust/parse/rust-parse-impl-attribute.hxx
b/gcc/rust/parse/rust-parse-impl-attribute.hxx
index 3db1cd5c6..d473c9e2b 100644
--- a/gcc/rust/parse/rust-parse-impl-attribute.hxx
+++ b/gcc/rust/parse/rust-parse-impl-attribute.hxx
@@ -336,6 +336,9 @@ Parser<ManagedTokenSource>::parse_attr_input ()
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;
diff --git a/gcc/rust/parse/rust-parse-impl-expr.hxx
b/gcc/rust/parse/rust-parse-impl-expr.hxx
index aaffa3dcb..cc5c40f71 100644
--- a/gcc/rust/parse/rust-parse-impl-expr.hxx
+++ b/gcc/rust/parse/rust-parse-impl-expr.hxx
@@ -342,6 +342,11 @@ Parser<ManagedTokenSource>::parse_literal_expr
(AST::AttrVec outer_attrs)
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);
@@ -2111,6 +2116,10 @@ Parser<ManagedTokenSource>::null_denotation_not_path (
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,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc
b/gcc/rust/typecheck/rust-hir-type-check-base.cc
index bacd5f5f4..9cded2dbc 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-base.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc
@@ -397,7 +397,39 @@ TypeCheckBase::resolve_literal (const
Analysis::NodeMapping &expr_mappings,
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;
diff --git a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
new file mode 100644
index 000000000..6d31e0a4e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
@@ -0,0 +1,7 @@
+#![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
diff --git a/gcc/testsuite/rust/execute/torture/c_string.rs
b/gcc/testsuite/rust/execute/torture/c_string.rs
new file mode 100644
index 000000000..58b481198
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/c_string.rs
@@ -0,0 +1,14 @@
+// { 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
+ }
+}
base-commit: 0144bafd9a9da4c220cc38c5f87b8a25d4c2c12d
--
2.54.0