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).