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

Reply via email to