public inbox for gcc-cvs@sourceware.org help / color / mirror / Atom feed
From: Thomas Schwinge <tschwinge@gcc.gnu.org> To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/rust/master] ast: Add const generic argument application type Date: Tue, 21 Jun 2022 10:34:00 +0000 (GMT) [thread overview] Message-ID: <20220621103400.13D2C3870901@sourceware.org> (raw) 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);
reply other threads:[~2022-06-21 10:34 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20220621103400.13D2C3870901@sourceware.org \ --to=tschwinge@gcc.gnu.org \ --cc=gcc-cvs@gcc.gnu.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
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).