public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] parser: Add base for parsing const generic application
@ 2022-06-21 10:33 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-06-21 10:33 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:0dbfdb5cfc5cf64de086a85aadc1e58b115fb7f6

commit 0dbfdb5cfc5cf64de086a85aadc1e58b115fb7f6
Author: Arthur Cohen <arthur.cohen@embecosm.com>
Date:   Wed Jun 15 12:25:33 2022 +0200

    parser: Add base for parsing const generic application
    
    1. Refactor const generic declaration
    
    The default value for const generics now benefits from using the same
    function as parsing a regular const generic expression
    
    2. `Parser::parse_type` should not always add errors
    
    In the case that we are parsing a const generic and not a type, we
    should not emit bogus errors from `parse_type` such as "unexpected token
    in type: LITERAL". Thus, we add a flag to the function to not always
    add errors to the error table
    
    3. Figure out how to deal with ambiguities
    
    In the following cases, parsing is ambiguous:
    
    ```rust
    let a: Foo<N>;
    ```
    
    What is N? Is it a type to be used as a generic argument? Is it a const
    value to be used for a const generic argument? We need to keep both
    possibilities and differentiate later during typechecking. We need to
    figure out if it would be better to keep the ambiguity in our future
    `ConstGenericArg` type (something like Kind::ConstVarOrType) or modify
    our current `AST::Type` to maybe get differentiated later as a const
    variable, which seems more annoying.
    
    Finally, since the const evaluation is not implemented yet, we are
    getting some bogus errors in the testcase. This commit simply serves as
    a necessary base: parsing const generics before we can apply them.

Diff:
---
 gcc/rust/ast/rust-path.h                       |   2 +
 gcc/rust/parse/rust-parse-impl.h               | 159 +++++++++++++++----------
 gcc/rust/parse/rust-parse.h                    |   3 +-
 gcc/testsuite/rust/compile/const_generics_3.rs |  26 ++++
 4 files changed, 128 insertions(+), 62 deletions(-)

diff --git a/gcc/rust/ast/rust-path.h b/gcc/rust/ast/rust-path.h
index 45d08bfbaa9..a1f88d83205 100644
--- a/gcc/rust/ast/rust-path.h
+++ b/gcc/rust/ast/rust-path.h
@@ -134,6 +134,8 @@ struct GenericArgs
   std::vector<Lifetime> lifetime_args;
   std::vector<std::unique_ptr<Type> > type_args;
   std::vector<GenericArgsBinding> binding_args;
+  // TODO: Handle const generics here as well.
+  // We can probably keep a vector of `Expr`s for this.
   Location locus;
 
 public:
diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h
index ae6ef4a9595..3a76d748d6a 100644
--- a/gcc/rust/parse/rust-parse-impl.h
+++ b/gcc/rust/parse/rust-parse-impl.h
@@ -2885,37 +2885,14 @@ Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token)
 	  {
 	    lexer.skip_token ();
 	    auto tok = lexer.peek_token ();
+	    auto default_expr = parse_const_generic_expression ();
 
-	    switch (tok->get_id ())
-	      {
-		case LEFT_CURLY: {
-		  auto block = parse_block_expr ();
-		  // pass block to `const_generic`
-		  break;
-		}
-		case IDENTIFIER: {
-		  auto ident = tok->get_str ();
-		  // pass identifier to `const_generic`
-		  break;
-		}
-	      case MINUS:
-	      case STRING_LITERAL:
-	      case CHAR_LITERAL:
-	      case INT_LITERAL:
-	      case FLOAT_LITERAL:
-	      case TRUE_LITERAL:
-		case FALSE_LITERAL: {
-		  auto literal = parse_literal_expr ();
-		  // pass literal to `const_generic`
-		  break;
-		}
-	      default:
-		rust_error_at (tok->get_locus (),
-			       "invalid token for start of default value for "
-			       "const generic parameter: expected %<block%>, "
-			       "%<identifier%> or %<literal%>, got %qs",
-			       token_id_to_str (tok->get_id ()));
-	      }
+	    if (!default_expr)
+	      rust_error_at (tok->get_locus (),
+			     "invalid token for start of default value for "
+			     "const generic parameter: expected %<block%>, "
+			     "%<identifier%> or %<literal%>, got %qs",
+			     token_id_to_str (tok->get_id ()));
 	  }
 
 	// param = std::unique_ptr<AST::GenericParam> (const_generic)
@@ -6182,6 +6159,39 @@ Parser<ManagedTokenSource>::parse_type_path ()
 			has_opening_scope_resolution);
 }
 
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::parse_const_generic_expression ()
+{
+  auto tok = lexer.peek_token ();
+  switch (tok->get_id ())
+    {
+    case LEFT_CURLY:
+      return parse_block_expr ();
+      case IDENTIFIER: {
+	lexer.skip_token ();
+
+	// TODO: This is ambiguous with regular generic types. We probably need
+	// to differentiate later on during type checking, and thus keep a
+	// special variant here
+
+	// return this
+	return std::unique_ptr<AST::IdentifierExpr> (
+	  new AST::IdentifierExpr (tok->get_str (), {}, tok->get_locus ()));
+      }
+    case MINUS:
+    case STRING_LITERAL:
+    case CHAR_LITERAL:
+    case INT_LITERAL:
+    case FLOAT_LITERAL:
+    case TRUE_LITERAL:
+    case FALSE_LITERAL:
+      return parse_literal_expr ();
+    default:
+      return nullptr;
+    }
+}
+
 // Parses the generic arguments in each path segment.
 template <typename ManagedTokenSource>
 AST::GenericArgs
@@ -6193,6 +6203,9 @@ Parser<ManagedTokenSource>::parse_path_generic_args ()
       return AST::GenericArgs::create_empty ();
     }
 
+  // We need to parse all lifetimes, then parse types and const generics in
+  // any order.
+
   // try to parse lifetimes first
   std::vector<AST::Lifetime> lifetime_args;
 
@@ -6222,35 +6235,42 @@ Parser<ManagedTokenSource>::parse_path_generic_args ()
 
   // try to parse types second
   std::vector<std::unique_ptr<AST::Type>> type_args;
+  std::vector<std::unique_ptr<AST::Expr>> const_args;
+
+  // TODO: Keep list of const expressions as well
 
   // TODO: think of better control structure
   t = lexer.peek_token ();
   while (!is_right_angle_tok (t->get_id ()))
     {
+      // FIXME: Is it fine to break if there is one binding? Can't there be
+      // bindings in between types?
+
       // ensure not binding being parsed as type accidently
       if (t->get_id () == IDENTIFIER
 	  && lexer.peek_token (1)->get_id () == EQUAL)
+	break;
+
+      auto type = parse_type (false);
+      if (type)
 	{
-	  break;
+	  type_args.emplace_back (std::move (type));
 	}
-
-      std::unique_ptr<AST::Type> type = parse_type ();
-      if (type == nullptr)
+      else
 	{
-	  // not necessarily an error
-	  break;
+	  auto const_generic_expr = parse_const_generic_expression ();
+	  if (const_generic_expr)
+	    const_args.emplace_back (std::move (const_generic_expr));
+	  else
+	    break;
 	}
 
-      type_args.push_back (std::move (type));
-
       // if next token isn't comma, then it must be end of list
       if (lexer.peek_token ()->get_id () != COMMA)
-	{
-	  break;
-	}
+	break;
+
       // skip comma
       lexer.skip_token ();
-
       t = lexer.peek_token ();
     }
 
@@ -8982,7 +9002,7 @@ Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
 // Parses a type (will further disambiguate any type).
 template <typename ManagedTokenSource>
 std::unique_ptr<AST::Type>
-Parser<ManagedTokenSource>::parse_type ()
+Parser<ManagedTokenSource>::parse_type (bool save_errors)
 {
   /* rules for all types:
    * NeverType:               '!'
@@ -9034,9 +9054,12 @@ Parser<ManagedTokenSource>::parse_type ()
 	AST::QualifiedPathInType path = parse_qualified_path_in_type ();
 	if (path.is_error ())
 	  {
-	    Error error (t->get_locus (),
-			 "failed to parse qualified path in type");
-	    add_error (std::move (error));
+	    if (save_errors)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse qualified path in type");
+		add_error (std::move (error));
+	      }
 
 	    return nullptr;
 	  }
@@ -9085,9 +9108,12 @@ Parser<ManagedTokenSource>::parse_type ()
 	AST::TypePath path = parse_type_path ();
 	if (path.is_error ())
 	  {
-	    Error error (t->get_locus (),
-			 "failed to parse path as first component of type");
-	    add_error (std::move (error));
+	    if (save_errors)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse path as first component of type");
+		add_error (std::move (error));
+	      }
 
 	    return nullptr;
 	  }
@@ -9103,10 +9129,13 @@ Parser<ManagedTokenSource>::parse_type ()
 	      AST::SimplePath macro_path = path.as_simple_path ();
 	      if (macro_path.is_empty ())
 		{
-		  Error error (t->get_locus (),
-			       "failed to parse simple path in macro "
-			       "invocation (for type)");
-		  add_error (std::move (error));
+		  if (save_errors)
+		    {
+		      Error error (t->get_locus (),
+				   "failed to parse simple path in macro "
+				   "invocation (for type)");
+		      add_error (std::move (error));
+		    }
 
 		  return nullptr;
 		}
@@ -9190,9 +9219,12 @@ Parser<ManagedTokenSource>::parse_type ()
 	  std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
 	  if (initial_bound == nullptr)
 	    {
-	      Error error (lexer.peek_token ()->get_locus (),
-			   "failed to parse ImplTraitType initial bound");
-	      add_error (std::move (error));
+	      if (save_errors)
+		{
+		  Error error (lexer.peek_token ()->get_locus (),
+			       "failed to parse ImplTraitType initial bound");
+		  add_error (std::move (error));
+		}
 
 	      return nullptr;
 	    }
@@ -9265,9 +9297,13 @@ Parser<ManagedTokenSource>::parse_type ()
 	      = parse_trait_bound ();
 	    if (initial_bound == nullptr)
 	      {
-		Error error (lexer.peek_token ()->get_locus (),
-			     "failed to parse TraitObjectType initial bound");
-		add_error (std::move (error));
+		if (save_errors)
+		  {
+		    Error error (
+		      lexer.peek_token ()->get_locus (),
+		      "failed to parse TraitObjectType initial bound");
+		    add_error (std::move (error));
+		  }
 
 		return nullptr;
 	      }
@@ -9313,8 +9349,9 @@ Parser<ManagedTokenSource>::parse_type ()
 	  }
       }
     default:
-      add_error (Error (t->get_locus (), "unrecognised token %qs in type",
-			t->get_token_description ()));
+      if (save_errors)
+	add_error (Error (t->get_locus (), "unrecognised token %qs in type",
+			  t->get_token_description ()));
 
       return nullptr;
     }
diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h
index d19bc71d310..f0aedfe6a22 100644
--- a/gcc/rust/parse/rust-parse.h
+++ b/gcc/rust/parse/rust-parse.h
@@ -138,7 +138,7 @@ public:
    */
   std::unique_ptr<AST::Stmt> parse_stmt (ParseRestrictions restrictions
 					 = ParseRestrictions ());
-  std::unique_ptr<AST::Type> parse_type ();
+  std::unique_ptr<AST::Type> parse_type (bool save_errors = true);
   std::unique_ptr<AST::ExternalItem> parse_external_item ();
   std::unique_ptr<AST::TraitItem> parse_trait_item ();
   std::unique_ptr<AST::InherentImplItem> parse_inherent_impl_item ();
@@ -177,6 +177,7 @@ private:
   AST::TypePath parse_type_path ();
   std::unique_ptr<AST::TypePathSegment> parse_type_path_segment ();
   AST::PathIdentSegment parse_path_ident_segment ();
+  std::unique_ptr<AST::Expr> parse_const_generic_expression ();
   AST::GenericArgs parse_path_generic_args ();
   AST::GenericArgsBinding parse_generic_args_binding ();
   AST::TypePathFunction parse_type_path_function (Location locus);
diff --git a/gcc/testsuite/rust/compile/const_generics_3.rs b/gcc/testsuite/rust/compile/const_generics_3.rs
new file mode 100644
index 00000000000..6a3a0fe27bf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_3.rs
@@ -0,0 +1,26 @@
+// { dg-additional-options "-w" }
+
+const M: usize = 4;
+
+struct Foo<T, const N: usize = 1> {
+    // FIXME: This error is bogus. But having it means parsing is valid!
+    value: [i32; N], // { dg-error "failed to find name: N" }
+}
+
+fn main() {
+    let foo = Foo::<i32> { value: [15] };
+    let foo = Foo::<i32, 2> { value: [15, 13] };
+    let foo: Foo<i32, 2> = Foo { value: [15, 13] };
+    let foo: Foo<i32, 2> = Foo::<i32, 2> { value: [15, 13] };
+    let foo: Foo<i32, { 1 + 1 }> = Foo { value: [15, 13] };
+    let foo = Foo::<i32, { 1 + 1 }> { value: [15, 13] };
+    let foo: Foo<i32, { 1 + 1 }> = Foo::<i32, { 1 + 1 }> { value: [15, 13] };
+    let foo: Foo<i32, M> = Foo::<i32, 4> {
+        value: [15, 13, 11, 9],
+    };
+
+    // FIXME: Add proper const typecheck errors here
+    let invalid_foo: Foo<i32, { 1 + 1 }> = Foo::<i32, 3> { value: [15, 13] };
+    let invalid_foo: Foo<i32, { 1 + 1 }> = Foo::<i32, M> { value: [15, 13] };
+    let invalid_foo: Foo<i32> = Foo::<i32, 2> { value: [15, 13] };
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-06-21 10:33 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-21 10:33 [gcc/devel/rust/master] parser: Add base for parsing const generic application Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).