From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 13D2C3870901; Tue, 21 Jun 2022 10:34:00 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 13D2C3870901 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Thomas Schwinge To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/rust/master] ast: Add const generic argument application type X-Act-Checkin: gcc X-Git-Author: Arthur Cohen X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: 76f7e45179de4e6e53caab104022cc0784ecd618 X-Git-Newrev: 5f3c8286b225f3c87bed920f3c3e003a90d98722 Message-Id: <20220621103400.13D2C3870901@sourceware.org> Date: Tue, 21 Jun 2022 10:34:00 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Jun 2022 10:34:00 -0000 https://gcc.gnu.org/g:5f3c8286b225f3c87bed920f3c3e003a90d98722 commit 5f3c8286b225f3c87bed920f3c3e003a90d98722 Author: Arthur Cohen 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 (``) 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; + * ``` + * + * 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 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 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 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_args; std::vector > type_args; std::vector binding_args; - // TODO: Handle const generics here as well. - // We can probably keep a vector of `Expr`s for this. + std::vector 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_args, std::vector > type_args, std::vector binding_args, + std::vector 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 (), std::vector > (), - std::vector ()); + std::vector (), + std::vector ()); } std::string as_string () const; @@ -203,6 +306,8 @@ public: std::vector &get_lifetime_args () { return lifetime_args; }; + std::vector &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_args - = std::vector (), - std::vector > type_args - = std::vector > (), - std::vector binding_args - = std::vector ()) + std::vector lifetime_args = {}, + std::vector > type_args = {}, + std::vector binding_args = {}, + std::vector 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 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 &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 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_args, std::vector > type_args, std::vector binding_args, + std::vector 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::parse_generic_param (EndTokenPred is_end_token) return nullptr; // optional default value - std::unique_ptr 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 %, " "% or %, 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 ( 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::parse_type_path () } template -std::unique_ptr +AST::ConstGenericArg Parser::parse_const_generic_expression () { auto tok = lexer.peek_token (); + std::unique_ptr 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 ( - 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::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::parse_path_generic_args () // try to parse types second std::vector> type_args; - std::vector> const_args; + std::vector const_args; // TODO: Keep list of const expressions as well @@ -6261,10 +6271,10 @@ Parser::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::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 parse_type_path_segment (); AST::PathIdentSegment parse_path_ident_segment (); - std::unique_ptr 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);