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

https://gcc.gnu.org/g:5f3c8286b225f3c87bed920f3c3e003a90d98722

commit 5f3c8286b225f3c87bed920f3c3e003a90d98722
Author: Arthur Cohen <arthur.cohen@embecosm.com>
Date:   Thu Jun 16 06:30:47 2022 +0200

    ast: Add const generic argument application type
    
    This commit adds a new type to the AST which contains const generic
    arguments. The `GenericArgs` structure now also keeps a vector of them,
    and the parser was modified accordingly. We need to handle these extra
    arguments in the various resolvers and type-checking phase.
    
    As pointed out before, const generic arguments are ambiguous at the AST
    level. Which is why this "sum type" contains a "Clear" variant when
    there is no ambiguity at all (`<5>`, `<{ 5 }>`, `<{ 5 + 15 }>` or
    `<{ N }>`) and an "Ambiguous" one if the value in question might be
    referring to a type argument (`<N>`) instead of a const generic
    argument.
    
    However, when parsing a const generic declaration, we can also have a
    default value. Thus, we need a mechanism to disambiguate
    `ConstGenericArg`s early.

Diff:
---
 gcc/rust/ast/rust-ast-full-test.cc |  14 +++
 gcc/rust/ast/rust-path.h           | 205 ++++++++++++++++++++++++++++---------
 gcc/rust/parse/rust-parse-impl.h   |  57 ++++++-----
 gcc/rust/parse/rust-parse.h        |   2 +-
 4 files changed, 205 insertions(+), 73 deletions(-)

diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc
index d2940dd4ec0..7f66ee45f6d 100644
--- a/gcc/rust/ast/rust-ast-full-test.cc
+++ b/gcc/rust/ast/rust-ast-full-test.cc
@@ -2609,6 +2609,20 @@ GenericArgs::as_string () const
 	}
     }
 
+  // const args
+  if (!const_args.empty ())
+    {
+      auto i = const_args.begin ();
+      auto e = const_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += i->as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
   // binding args
   if (!binding_args.empty ())
     {
diff --git a/gcc/rust/ast/rust-path.h b/gcc/rust/ast/rust-path.h
index a1f88d83205..5642226afce 100644
--- a/gcc/rust/ast/rust-path.h
+++ b/gcc/rust/ast/rust-path.h
@@ -22,6 +22,7 @@
  * for virtually all AST-related functionality. */
 
 #include "rust-ast.h"
+#include "system.h"
 
 namespace Rust {
 namespace AST {
@@ -128,14 +129,112 @@ public:
   Identifier get_identifier () const { return identifier; }
 };
 
+/* Class representing a const generic application */
+class ConstGenericArg
+{
+public:
+  /**
+   * const generic arguments cannot always be differentiated with generic type
+   * arguments during parsing, e.g:
+   * ```rust
+   * let a: Foo<N>;
+   * ```
+   *
+   * Is N a type? A constant defined elsewhere? The parser cannot know, and must
+   * not draw any conclusions. We must wait until later passes of the compiler
+   * to decide whether this refers to a constant item or a type.
+   *
+   * On the other hand, simple expressions like literals or block expressions
+   * will always be constant expressions: There is no ambiguity at all.
+   */
+  enum class Kind
+  {
+    Error,
+    Clear,
+    Ambiguous,
+  };
+
+  static ConstGenericArg create_error ()
+  {
+    return ConstGenericArg (nullptr, "", Kind::Error);
+  }
+
+  ConstGenericArg (std::unique_ptr<Expr> expression)
+    : expression (std::move (expression)), path (""), kind (Kind::Clear)
+  {}
+
+  ConstGenericArg (Identifier path)
+    : expression (nullptr), path (path), kind (Kind::Ambiguous)
+  {}
+
+  ConstGenericArg (const ConstGenericArg &other)
+    : path (other.path), kind (other.kind)
+  {
+    if (other.expression)
+      expression = other.expression->clone_expr ();
+  }
+
+  ConstGenericArg operator= (const ConstGenericArg &other)
+  {
+    kind = other.kind;
+    path = other.path;
+
+    if (other.expression)
+      expression = other.expression->clone_expr ();
+
+    return *this;
+  }
+
+  bool is_error () const { return kind == Kind::Error; }
+
+  Kind get_kind () const { return kind; }
+
+  std::string as_string () const
+  {
+    switch (get_kind ())
+      {
+      case Kind::Error:
+	gcc_unreachable ();
+      case Kind::Ambiguous:
+	return "Ambiguous: " + path;
+      case Kind::Clear:
+	return "Clear: { " + expression->as_string () + " }";
+      }
+
+    return "";
+  }
+
+private:
+  ConstGenericArg (std::unique_ptr<AST::Expr> expression, Identifier path,
+		   Kind kind)
+    : expression (std::move (expression)), path (std::move (path)), kind (kind)
+  {}
+
+  /**
+   * Expression associated with a `Clear` const generic application
+   * A null pointer here is allowed in the case that the const argument is
+   * ambiguous.
+   */
+  std::unique_ptr<Expr> expression;
+
+  /**
+   * Optional path which cannot be differentiated between a constant item and
+   * a type. Only used for `Ambiguous` const generic arguments, otherwise
+   * empty.
+   */
+  Identifier path;
+
+  /* Which kind of const generic application are we dealing with */
+  Kind kind;
+};
+
 // Generic arguments allowed in each path expression segment - inline?
 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.
+  std::vector<ConstGenericArg> const_args;
   Location locus;
 
 public:
@@ -143,22 +242,24 @@ public:
   bool has_generic_args () const
   {
     return !(lifetime_args.empty () && type_args.empty ()
-	     && binding_args.empty ());
+	     && binding_args.empty () && const_args.empty ());
   }
 
   GenericArgs (std::vector<Lifetime> lifetime_args,
 	       std::vector<std::unique_ptr<Type> > type_args,
 	       std::vector<GenericArgsBinding> binding_args,
+	       std::vector<ConstGenericArg> const_args,
 	       Location locus = Location ())
     : lifetime_args (std::move (lifetime_args)),
       type_args (std::move (type_args)),
-      binding_args (std::move (binding_args)), locus (locus)
+      binding_args (std::move (binding_args)),
+      const_args (std::move (const_args)), locus (locus)
   {}
 
   // copy constructor with vector clone
   GenericArgs (GenericArgs const &other)
     : lifetime_args (other.lifetime_args), binding_args (other.binding_args),
-      locus (other.locus)
+      const_args (other.const_args), locus (other.locus)
   {
     type_args.reserve (other.type_args.size ());
     for (const auto &e : other.type_args)
@@ -172,6 +273,7 @@ public:
   {
     lifetime_args = other.lifetime_args;
     binding_args = other.binding_args;
+    const_args = other.const_args;
     locus = other.locus;
 
     type_args.reserve (other.type_args.size ());
@@ -190,7 +292,8 @@ public:
   {
     return GenericArgs (std::vector<Lifetime> (),
 			std::vector<std::unique_ptr<Type> > (),
-			std::vector<GenericArgsBinding> ());
+			std::vector<GenericArgsBinding> (),
+			std::vector<ConstGenericArg> ());
   }
 
   std::string as_string () const;
@@ -203,6 +306,8 @@ public:
 
   std::vector<Lifetime> &get_lifetime_args () { return lifetime_args; };
 
+  std::vector<ConstGenericArg> &get_const_args () { return const_args; };
+
   Location get_locus () { return locus; }
 };
 
@@ -231,16 +336,14 @@ public:
   /* Constructor for segment with generic arguments (from segment name and all
    * args) */
   PathExprSegment (std::string segment_name, Location locus,
-		   std::vector<Lifetime> lifetime_args
-		   = std::vector<Lifetime> (),
-		   std::vector<std::unique_ptr<Type> > type_args
-		   = std::vector<std::unique_ptr<Type> > (),
-		   std::vector<GenericArgsBinding> binding_args
-		   = std::vector<GenericArgsBinding> ())
+		   std::vector<Lifetime> lifetime_args = {},
+		   std::vector<std::unique_ptr<Type> > type_args = {},
+		   std::vector<GenericArgsBinding> binding_args = {},
+		   std::vector<ConstGenericArg> const_args = {})
     : segment_name (PathIdentSegment (std::move (segment_name), locus)),
-      generic_args (GenericArgs (std::move (lifetime_args),
-				 std::move (type_args),
-				 std::move (binding_args))),
+      generic_args (
+	GenericArgs (std::move (lifetime_args), std::move (type_args),
+		     std::move (binding_args), std::move (const_args))),
       locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
   {}
 
@@ -284,7 +387,8 @@ public:
   }
 };
 
-// AST node representing a pattern that involves a "path" - abstract base class
+// AST node representing a pattern that involves a "path" - abstract base
+// class
 class PathPattern : public Pattern
 {
   std::vector<PathExprSegment> segments;
@@ -297,8 +401,8 @@ protected:
   // Returns whether path has segments.
   bool has_segments () const { return !segments.empty (); }
 
-  /* Converts path segments to their equivalent SimplePath segments if possible,
-   * and creates a SimplePath from them. */
+  /* Converts path segments to their equivalent SimplePath segments if
+   * possible, and creates a SimplePath from them. */
   SimplePath convert_to_simple_path (bool with_opening_scope_resolution) const;
 
   // Removes all segments of the path.
@@ -320,8 +424,8 @@ public:
   const std::vector<PathExprSegment> &get_segments () const { return segments; }
 };
 
-/* AST node representing a path-in-expression pattern (path that allows generic
- * arguments) */
+/* AST node representing a path-in-expression pattern (path that allows
+ * generic arguments) */
 class PathInExpression : public PathPattern, public PathExpr
 {
   std::vector<Attribute> outer_attrs;
@@ -356,9 +460,10 @@ public:
   SimplePath as_simple_path () const
   {
     /* delegate to parent class as can't access segments. however,
-     * QualifiedPathInExpression conversion to simple path wouldn't make sense,
-     * so the method in the parent class should be protected, not public. Have
-     * to pass in opening scope resolution as parent class has no access to it.
+     * QualifiedPathInExpression conversion to simple path wouldn't make
+     * sense, so the method in the parent class should be protected, not
+     * public. Have to pass in opening scope resolution as parent class has no
+     * access to it.
      */
     return convert_to_simple_path (has_opening_scope_resolution);
   }
@@ -389,15 +494,15 @@ public:
   NodeId get_pattern_node_id () const override final { return get_node_id (); }
 
 protected:
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
   PathInExpression *clone_pattern_impl () const final override
   {
     return clone_path_in_expression_impl ();
   }
 
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
   PathInExpression *clone_expr_without_block_impl () const final override
   {
     return clone_path_in_expression_impl ();
@@ -431,7 +536,8 @@ protected:
   bool has_separating_scope_resolution;
   NodeId node_id;
 
-  // Clone function implementation - not pure virtual as overrided by subclasses
+  // Clone function implementation - not pure virtual as overrided by
+  // subclasses
   virtual TypePathSegment *clone_type_path_segment_impl () const
   {
     return new TypePathSegment (*this);
@@ -465,8 +571,8 @@ public:
 
   virtual std::string as_string () const { return ident_segment.as_string (); }
 
-  /* Returns whether the type path segment is in an error state. May be virtual
-   * in future. */
+  /* Returns whether the type path segment is in an error state. May be
+   * virtual in future. */
   bool is_error () const { return ident_segment.is_error (); }
 
   /* Returns whether segment is identifier only (as opposed to generic args or
@@ -526,12 +632,13 @@ public:
 			  std::vector<Lifetime> lifetime_args,
 			  std::vector<std::unique_ptr<Type> > type_args,
 			  std::vector<GenericArgsBinding> binding_args,
+			  std::vector<ConstGenericArg> const_args,
 			  Location locus)
     : TypePathSegment (std::move (segment_name),
 		       has_separating_scope_resolution, locus),
-      generic_args (GenericArgs (std::move (lifetime_args),
-				 std::move (type_args),
-				 std::move (binding_args)))
+      generic_args (
+	GenericArgs (std::move (lifetime_args), std::move (type_args),
+		     std::move (binding_args), std::move (const_args)))
   {}
 
   std::string as_string () const override;
@@ -709,16 +816,16 @@ class TypePath : public TypeNoBounds
   Location locus;
 
 protected:
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
   TypePath *clone_type_no_bounds_impl () const override
   {
     return new TypePath (*this);
   }
 
 public:
-  /* Returns whether the TypePath has an opening scope resolution operator (i.e.
-   * is global path or crate-relative path, not module-relative) */
+  /* Returns whether the TypePath has an opening scope resolution operator
+   * (i.e. is global path or crate-relative path, not module-relative) */
   bool has_opening_scope_resolution_op () const
   {
     return has_opening_scope_resolution;
@@ -900,8 +1007,8 @@ public:
       _node_id (Analysis::Mappings::get ()->get_next_node_id ())
   {}
 
-  /* TODO: maybe make a shortcut constructor that has QualifiedPathType elements
-   * as params */
+  /* TODO: maybe make a shortcut constructor that has QualifiedPathType
+   * elements as params */
 
   // Returns whether qualified path in expression is in an error state.
   bool is_error () const { return path_type.is_error (); }
@@ -944,15 +1051,15 @@ public:
   NodeId get_pattern_node_id () const override final { return get_node_id (); }
 
 protected:
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
   QualifiedPathInExpression *clone_pattern_impl () const final override
   {
     return clone_qual_path_in_expression_impl ();
   }
 
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
   QualifiedPathInExpression *
   clone_expr_without_block_impl () const final override
   {
@@ -966,8 +1073,8 @@ protected:
   }
 };
 
-/* Represents a qualified path in a type; used for disambiguating trait function
- * calls */
+/* Represents a qualified path in a type; used for disambiguating trait
+ * function calls */
 class QualifiedPathInType : public TypeNoBounds
 {
   QualifiedPathType path_type;
@@ -976,8 +1083,8 @@ class QualifiedPathInType : public TypeNoBounds
   Location locus;
 
 protected:
-  /* Use covariance to implement clone function as returning this object rather
-   * than base */
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
   QualifiedPathInType *clone_type_no_bounds_impl () const override
   {
     return new QualifiedPathInType (*this);
@@ -994,8 +1101,8 @@ public:
       segments (std::move (path_segments)), locus (locus)
   {}
 
-  /* TODO: maybe make a shortcut constructor that has QualifiedPathType elements
-   * as params */
+  /* TODO: maybe make a shortcut constructor that has QualifiedPathType
+   * elements as params */
 
   // Copy constructor with vector clone
   QualifiedPathInType (QualifiedPathInType const &other)
diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h
index b9f031d0049..c2efd535ade 100644
--- a/gcc/rust/parse/rust-parse-impl.h
+++ b/gcc/rust/parse/rust-parse-impl.h
@@ -2878,25 +2878,28 @@ Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token)
 	  return nullptr;
 
 	// optional default value
-	std::unique_ptr<AST::Expr> default_expr = nullptr;
+	auto default_expr = AST::ConstGenericArg::create_error ();
 	if (lexer.peek_token ()->get_id () == EQUAL)
 	  {
 	    lexer.skip_token ();
 	    auto tok = lexer.peek_token ();
 	    default_expr = parse_const_generic_expression ();
 
-	    if (!default_expr)
+	    if (default_expr.is_error ())
 	      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 ()));
+
+	    // TODO: At this point, we *know* that we are parsing a const
+	    // expression. We should figure out how to disambiguate the default
+	    // expr in the case of `const N: usize = M`
 	  }
 
 	param = std::unique_ptr<AST::ConstGenericParam> (
 	  new AST::ConstGenericParam (name_token->get_str (), std::move (type),
-				      std::move (default_expr),
-				      std::move (outer_attrs),
+				      nullptr, std::move (outer_attrs),
 				      token->get_locus ()));
 
 	break;
@@ -6162,25 +6165,26 @@ Parser<ManagedTokenSource>::parse_type_path ()
 }
 
 template <typename ManagedTokenSource>
-std::unique_ptr<AST::Expr>
+AST::ConstGenericArg
 Parser<ManagedTokenSource>::parse_const_generic_expression ()
 {
   auto tok = lexer.peek_token ();
+  std::unique_ptr<AST::Expr> expr = nullptr;
+
   switch (tok->get_id ())
     {
-    case LEFT_CURLY:
-      return parse_block_expr ();
-      case IDENTIFIER: {
-	lexer.skip_token ();
+    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
+      // 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 ()));
-      }
+      // FIXME: We need locus here as well
+      return AST::ConstGenericArg (tok->get_str ());
+    case LEFT_CURLY:
+      expr = parse_block_expr ();
+      break;
     case MINUS:
     case STRING_LITERAL:
     case CHAR_LITERAL:
@@ -6188,10 +6192,16 @@ Parser<ManagedTokenSource>::parse_const_generic_expression ()
     case FLOAT_LITERAL:
     case TRUE_LITERAL:
     case FALSE_LITERAL:
-      return parse_literal_expr ();
+      expr = parse_literal_expr ();
+      break;
     default:
-      return nullptr;
+      expr = nullptr;
     }
+
+  if (!expr)
+    return AST::ConstGenericArg::create_error ();
+
+  return AST::ConstGenericArg (std::move (expr));
 }
 
 // Parses the generic arguments in each path segment.
@@ -6237,7 +6247,7 @@ 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;
+  std::vector<AST::ConstGenericArg> const_args;
 
   // TODO: Keep list of const expressions as well
 
@@ -6261,10 +6271,10 @@ Parser<ManagedTokenSource>::parse_path_generic_args ()
       else
 	{
 	  auto const_generic_expr = parse_const_generic_expression ();
-	  if (const_generic_expr)
-	    const_args.emplace_back (std::move (const_generic_expr));
-	  else
+	  if (const_generic_expr.is_error ())
 	    break;
+	  else
+	    const_args.emplace_back (std::move (const_generic_expr));
 	}
 
       // if next token isn't comma, then it must be end of list
@@ -6315,7 +6325,8 @@ Parser<ManagedTokenSource>::parse_path_generic_args ()
   binding_args.shrink_to_fit ();
 
   return AST::GenericArgs (std::move (lifetime_args), std::move (type_args),
-			   std::move (binding_args), locus);
+			   std::move (binding_args), std::move (const_args),
+			   locus);
 }
 
 // Parses a binding in a generic args path segment.
diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h
index f0aedfe6a22..fa88f8eff9d 100644
--- a/gcc/rust/parse/rust-parse.h
+++ b/gcc/rust/parse/rust-parse.h
@@ -177,7 +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::ConstGenericArg parse_const_generic_expression ();
   AST::GenericArgs parse_path_generic_args ();
   AST::GenericArgsBinding parse_generic_args_binding ();
   AST::TypePathFunction parse_type_path_function (Location locus);


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

only message in thread, other threads:[~2022-06-21 10:34 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:34 [gcc/devel/rust/master] ast: Add const generic argument application type 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).