From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 6EC9A3870901; Tue, 21 Jun 2022 10:33:14 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6EC9A3870901 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] parser: Add base for parsing const generic application X-Act-Checkin: gcc X-Git-Author: Arthur Cohen X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: 08e407e977e78bfebb2faa71be377c58369b8b0d X-Git-Newrev: 0dbfdb5cfc5cf64de086a85aadc1e58b115fb7f6 Message-Id: <20220621103314.6EC9A3870901@sourceware.org> Date: Tue, 21 Jun 2022 10:33:14 +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:33:14 -0000 https://gcc.gnu.org/g:0dbfdb5cfc5cf64de086a85aadc1e58b115fb7f6 commit 0dbfdb5cfc5cf64de086a85aadc1e58b115fb7f6 Author: Arthur Cohen 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; ``` 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_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. 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::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 %, " - "% or %, 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 %, " + "% or %, got %qs", + token_id_to_str (tok->get_id ())); } // param = std::unique_ptr (const_generic) @@ -6182,6 +6159,39 @@ Parser::parse_type_path () has_opening_scope_resolution); } +template +std::unique_ptr +Parser::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 ( + 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 AST::GenericArgs @@ -6193,6 +6203,9 @@ Parser::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 lifetime_args; @@ -6222,35 +6235,42 @@ Parser::parse_path_generic_args () // try to parse types second std::vector> type_args; + std::vector> 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 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::parse_grouped_or_tuple_expr ( // Parses a type (will further disambiguate any type). template std::unique_ptr -Parser::parse_type () +Parser::parse_type (bool save_errors) { /* rules for all types: * NeverType: '!' @@ -9034,9 +9054,12 @@ Parser::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::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::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::parse_type () std::unique_ptr 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::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::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 parse_stmt (ParseRestrictions restrictions = ParseRestrictions ()); - std::unique_ptr parse_type (); + std::unique_ptr parse_type (bool save_errors = true); std::unique_ptr parse_external_item (); std::unique_ptr parse_trait_item (); std::unique_ptr parse_inherent_impl_item (); @@ -177,6 +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::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 { + // 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:: { value: [15] }; + let foo = Foo:: { value: [15, 13] }; + let foo: Foo = Foo { value: [15, 13] }; + let foo: Foo = Foo:: { value: [15, 13] }; + let foo: Foo = Foo { value: [15, 13] }; + let foo = Foo:: { value: [15, 13] }; + let foo: Foo = Foo:: { value: [15, 13] }; + let foo: Foo = Foo:: { + value: [15, 13, 11, 9], + }; + + // FIXME: Add proper const typecheck errors here + let invalid_foo: Foo = Foo:: { value: [15, 13] }; + let invalid_foo: Foo = Foo:: { value: [15, 13] }; + let invalid_foo: Foo = Foo:: { value: [15, 13] }; +}