Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2223299/?format=api
{ "id": 2223299, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2223299/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260414231905.191063-35-arthur.cohen@embecosm.com/", "project": { "id": 17, "url": "http://patchwork.ozlabs.org/api/1.1/projects/17/?format=api", "name": "GNU Compiler Collection", "link_name": "gcc", "list_id": "gcc-patches.gcc.gnu.org", "list_email": "gcc-patches@gcc.gnu.org", "web_url": null, "scm_url": null, "webscm_url": null }, "msgid": "<20260414231905.191063-35-arthur.cohen@embecosm.com>", "date": "2026-04-14T23:18:56", "name": "[COMMITTED,35/43] gccrs: parse all function qualifiers before generating error", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "94f1982991d99a60617369f61e5e630cda623ecb", "submitter": { "id": 83476, "url": "http://patchwork.ozlabs.org/api/1.1/people/83476/?format=api", "name": "Arthur Cohen", "email": "arthur.cohen@embecosm.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260414231905.191063-35-arthur.cohen@embecosm.com/mbox/", "series": [ { "id": 499900, "url": "http://patchwork.ozlabs.org/api/1.1/series/499900/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=499900", "date": "2026-04-14T23:18:24", "name": "[COMMITTED,01/43] gccrs: testsuite:Add a testcase for setup_associated_types", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/499900/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2223299/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2223299/checks/", "tags": {}, "headers": { "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=embecosm.com header.i=@embecosm.com header.a=rsa-sha256\n header.s=google header.b=AagYPME5;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=embecosm.com header.i=@embecosm.com header.a=rsa-sha256\n header.s=google header.b=AagYPME5", "sourceware.org;\n dmarc=none (p=none dis=none) header.from=embecosm.com", "sourceware.org; spf=pass smtp.mailfrom=embecosm.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=209.85.128.68" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fwL8R51t0z1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 15 Apr 2026 09:29:27 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id B33644BA23E9\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 14 Apr 2026 23:29:25 +0000 (GMT)", "from mail-wm1-f68.google.com (mail-wm1-f68.google.com\n [209.85.128.68])\n by sourceware.org (Postfix) with ESMTPS id CD9DA4BA9009\n for <gcc-patches@gcc.gnu.org>; Tue, 14 Apr 2026 23:15:04 +0000 (GMT)", "by mail-wm1-f68.google.com with SMTP id\n 5b1f17b1804b1-4887fd35e60so43192325e9.2\n for <gcc-patches@gcc.gnu.org>; Tue, 14 Apr 2026 16:15:04 -0700 (PDT)", "from platypus.localdomain (176-147-231-59.abo.bbox.fr.\n [176.147.231.59]) by smtp.gmail.com with ESMTPSA id\n 5b1f17b1804b1-488f1dd8806sm3325945e9.3.2026.04.14.16.15.01\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 14 Apr 2026 16:15:01 -0700 (PDT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org B33644BA23E9", "OpenDKIM Filter v2.11.0 sourceware.org CD9DA4BA9009" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org CD9DA4BA9009", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org CD9DA4BA9009", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776208505; cv=none;\n b=cbqPMwEtgnrdtsVjFCUhZg/wQgn+BSRFLPgToHiXUv47X0J8TKIw3faonnHEyCHubYvnwxmh9f7nyRjzJ67Y9hRpeVU8uc+HU9Nqs0hlvMF2VT8Woc7rElOhq1gyDpx8LKiKuu7j3HYJmPIZhqfJIAQdyRyFp3xYf/X4Bq/6SpU=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776208505; c=relaxed/simple;\n bh=EC7bOvc6eKsHRhA1xm9tKZH7dXWTPNhF2wl4rd4a8Zc=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=V228wklqyNXjmC/iEadNaFRZwWSOBETfxvuUOyalMMyEpgmvOTIT5VgX9L77fGXVjMsbohJS8YVVA6C0Yz6jd3Vt1EdKMtNym03iJ9gk8Wx0W05+BZaI6RLlA7buF+mpt9jKbzSZLAL+Uuasnx9BSodBTp181RXkGlPBIwLrjt0=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=embecosm.com; s=google; t=1776208503; x=1776813303; darn=gcc.gnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=eTiZdyrZzMMFC0WbAxauKSjAeyOyOvJ3LyNzUekbemg=;\n b=AagYPME5CoxS2lqG3KS2t0HRAjuScR8urQ+xK1jKurEbabG//SwpmAdRuUQ7PfLBzU\n Lq11QqaQaDE+qYBDRz8Nk5XEme4rNrTYAlkSKX69/30O/DhFCg/c5/4N95YYdrPPCPZ8\n +BUBKqRBLk32tL1bU/I0XMGWwQVHHk9pJSCCNCHmhjfb/u4/LHo7i4FAekwh5nzMVd4n\n ligd7mcyrMpg6KpmfmXTGKhP0P3KIqjHLCknyYltl4eQQsWddJXcvUQUi3LKJ9hOtT1z\n poeNz4J4HhQaQAbcbdZxujLUCK5Z+NZha+NLy3ECpC4Y9+fh2Et89Pggw69ND4QoxKFa\n OZVg==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776208503; x=1776813303;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=eTiZdyrZzMMFC0WbAxauKSjAeyOyOvJ3LyNzUekbemg=;\n b=EO4PQQzK9IepEB3u7Ysh4bIBYMvWMREbZVPq9TdrLrJ9MeRg86mlhLUcEOUcJPZuBg\n r2mEucjOIXsrIDVl4bKKk/ENrYEUAW7lS9aZiMsLP71x8kXblkbxMdgGnqWvvnW9XyKV\n ggvmuYb8bKd16FnXkSBOsCKIGqHqVAcMwAjImu0B5KrU75VduRUQPlbKoRITRzlJC/+U\n fW7UWheZDxicMhJih0EYNeAFAlESZynVmUTDTBVLeN/h7m02i9MZtuDk52Z0b6tP/2W6\n 8WiC6U9ir1pkb93AB6cvgv7l8Iqr3VKHWR9sPJlV3QMTTgJWge+kzyGLCUuvfCU0Vq+Y\n GDWg==", "X-Gm-Message-State": "AOJu0YzE4QHtcepdnDE1S4nWdrkYsiPO0KwCBqRtXrGTlOO0XGsaCTny\n 2HswVF8aDx4Lo59YYCtTe4pVBRgn1yeqRSlUWiZFETN5MU5LW1Cz0VC35jT17pyce0XTvYAfmno\n Gs5WX3Zo/", "X-Gm-Gg": "AeBDievwIFWAe/MCMFmnpeg328KwQPT9onxJzyTXgTH1xvjr8Yn/S8JaoQeqnJkni2J\n MVIlgw5u2NcZFiUt7CI28TJ3K5doTa/4HME5xCoz6kLnutt1+aljIk3IZVzu5yyTp6sv06V3/ds\n jl0XwZ53pRfQW9COwI3ktJfMSygVhV7K++l1W7Du6gi72CUrt9vwAOkO+cG/Zb0IzakpaWBiYZk\n XIpypAFrwU50QoryHs2Dvk1q4lPKfH7yOJ+3R7R36jLnPj8x5kJ7de7A6s2EeoVfajSf0PuUpEw\n Uf2Svpi4m71Cfj7vO1goTCABokIcZxm+ccOqmuwdjQ2SNnT5rp3fNpoFvvLjpFdrovVfkR7kQNq\n uuVOrlD2sPQicnN7eF9v1c/H32lOZgEczlNsxz0sTwQJ6qmCi+8iQqLll5sM00dktV0PxxBneSp\n FyKgw6d2pNg4kEtECJ1mUJfJYq7uJeaWzTBf1WIRNn6YOVyT/OIHgcGs2e+fqOL9QUgal5Rw7F0\n yRMqf7IUTo+SA6J", "X-Received": "by 2002:a05:600c:8b38:b0:488:be58:bb77 with SMTP id\n 5b1f17b1804b1-488d6890ee3mr241176525e9.30.1776208503483;\n Tue, 14 Apr 2026 16:15:03 -0700 (PDT)", "From": "arthur.cohen@embecosm.com", "To": "gcc-patches@gcc.gnu.org", "Cc": "gcc-rust@gcc.gnu.org,\n\tPhilipp Gesang <phg@phi-gamma.net>", "Subject": "[COMMITTED 35/43] gccrs: parse all function qualifiers before\n generating error", "Date": "Wed, 15 Apr 2026 01:18:56 +0200", "Message-ID": "<20260414231905.191063-35-arthur.cohen@embecosm.com>", "X-Mailer": "git-send-email 2.50.1", "In-Reply-To": "<20260414231905.191063-1-arthur.cohen@embecosm.com>", "References": "<20260414231905.191063-1-arthur.cohen@embecosm.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "gcc-patches@gcc.gnu.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>", "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>", "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>", "List-Post": "<mailto:gcc-patches@gcc.gnu.org>", "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>", "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>", "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org" }, "content": "From: Philipp Gesang <phg@phi-gamma.net>\n\nInstead of erroring out on the first misplaced qualifier, parse\nas many qualifiers as possible to allow for more helpful error\nmessage giving the correct order the qualifiers should be in.\nDuplicate qualifiers are a kind of a special case and generate a\nseparate error message.\n\ngcc/rust/ChangeLog\n\t* parse/rust-parse-impl.hxx (parse_function_qualifiers)\n\tCollect qualifiers into vector before generating the\n\terror message\n\t* parse/rust-parse.h: (parse_function_qualifiers) Make\n\tfunction fallible\n\ngcc/testsuite/ChangeLog:\n\t* rust/compile/func-qualifier-default.rs:\n\tAdapt to change in compiler messages\n\t* rust/compile/func-qualifier-order.rs: Renamed existing\n\ttest file for clarity (from)\n\t* rust/compile/func-qualifier-order-1.rs: Renamed existing\n\ttest file (to)\n\t* rust/compile/func-qualifier-order-2.rs: New test file\n\t* rust/compile/func-qualifier-order-3.rs: New test file\n\nSigned-off-by: Philipp Gesang <phg@phi-gamma.net>\n---\n gcc/rust/parse/rust-parse-impl.hxx | 218 ++++++++++++++----\n gcc/rust/parse/rust-parse.h | 5 +-\n .../rust/compile/func-qualifier-default.rs | 5 +-\n ...ier-order.rs => func-qualifier-order-1.rs} | 0\n .../rust/compile/func-qualifier-order-2.rs | 6 +\n .../rust/compile/func-qualifier-order-3.rs | 14 ++\n 6 files changed, 195 insertions(+), 53 deletions(-)\n rename gcc/testsuite/rust/compile/{func-qualifier-order.rs => func-qualifier-order-1.rs} (100%)\n create mode 100644 gcc/testsuite/rust/compile/func-qualifier-order-2.rs\n create mode 100644 gcc/testsuite/rust/compile/func-qualifier-order-3.rs", "diff": "diff --git a/gcc/rust/parse/rust-parse-impl.hxx b/gcc/rust/parse/rust-parse-impl.hxx\nindex 8820e3555e0..fc9f45a8e5c 100644\n--- a/gcc/rust/parse/rust-parse-impl.hxx\n+++ b/gcc/rust/parse/rust-parse-impl.hxx\n@@ -1559,7 +1559,9 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,\n {\n location_t locus = lexer.peek_token ()->get_locus ();\n // Get qualifiers for function if they exist\n- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();\n+ auto qualifiers = parse_function_qualifiers ();\n+ if (!qualifiers)\n+ return nullptr;\n \n skip_token (FN_KW);\n \n@@ -1635,17 +1637,16 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,\n body = std::move (block_expr.value ());\n }\n \n- return std::unique_ptr<AST::Function> (\n- new AST::Function (std::move (function_name), std::move (qualifiers),\n-\t\t std::move (generic_params), std::move (function_params),\n-\t\t std::move (return_type), std::move (where_clause),\n-\t\t std::move (body), std::move (vis),\n-\t\t std::move (outer_attrs), locus, is_external));\n+ return std::unique_ptr<AST::Function> (new AST::Function (\n+ std::move (function_name), std::move (*qualifiers.value ()),\n+ std::move (generic_params), std::move (function_params),\n+ std::move (return_type), std::move (where_clause), std::move (body),\n+ std::move (vis), std::move (outer_attrs), locus, is_external));\n }\n \n // Parses function or method qualifiers (i.e. const, unsafe, and extern).\n template <typename ManagedTokenSource>\n-AST::FunctionQualifiers\n+tl::expected<std::unique_ptr<AST::FunctionQualifiers>, Parse::Error::Node>\n Parser<ManagedTokenSource>::parse_function_qualifiers ()\n {\n Default default_status = Default::No;\n@@ -1655,51 +1656,165 @@ Parser<ManagedTokenSource>::parse_function_qualifiers ()\n bool has_extern = false;\n std::string abi;\n \n+ // collect all qualifiers before checking the order to allow for a better\n+ // error message\n+ std::vector<TokenId> found_order;\n+\n const_TokenPtr t;\n location_t locus = lexer.peek_token ()->get_locus ();\n- // Check in order of default, const, async, unsafe, extern\n- if (lexer.peek_token ()->get_id () == IDENTIFIER\n- && lexer.peek_token ()->get_str () == Values::WeakKeywords::DEFAULT)\n- {\n- default_status = Default::Yes;\n- lexer.skip_token();\n- }\n \n- if (lexer.peek_token ()->get_id () == CONST)\n+ // this will terminate on duplicates or the first non-qualifier token\n+ while (true)\n {\n- lexer.skip_token ();\n- const_status = Const::Yes;\n- }\n+ const TokenId token_id = lexer.peek_token ()->get_id ();\n \n- if (lexer.peek_token ()->get_id () == ASYNC)\n- {\n- lexer.skip_token ();\n- async_status = Async::Yes;\n- }\n+ if (std::find (found_order.cbegin (), found_order.cend (), token_id)\n+\t != found_order.cend ())\n+\t{\n+\t // qualifiers mustn't appear twice\n+\t Error error (lexer.peek_token ()->get_locus (),\n+\t\t \"encountered duplicate function qualifier %qs\",\n+\t\t lexer.peek_token ()->get_token_description ());\n+\t add_error (std::move (error));\n \n- if (lexer.peek_token ()->get_id () == UNSAFE)\n- {\n+\t return tl::unexpected<Parse::Error::Node> (\n+\t Parse::Error::Node::MALFORMED);\n+\t}\n+\n+ switch (token_id)\n+\t{\n+\tcase IDENTIFIER:\n+\t if (lexer.peek_token ()->get_str () != Values::WeakKeywords::DEFAULT)\n+\t {\n+\t // only \"default\" is valid in this context\n+\t goto done;\n+\t }\n+\t default_status = Default::Yes;\n+\t break;\n+\tcase CONST:\n+\t const_status = Const::Yes;\n+\t break;\n+\tcase ASYNC:\n+\t async_status = Async::Yes;\n+\t break;\n+\tcase UNSAFE:\n+\t unsafe_status = Unsafety::Unsafe;\n+\t break;\n+\tcase EXTERN_KW:\n+\t {\n+\t has_extern = true;\n+\t // detect optional abi name\n+\t lexer.skip_token ();\n+\t const_TokenPtr next_tok = lexer.peek_token ();\n+\t if (next_tok->get_id () == STRING_LITERAL)\n+\t {\n+\t\tabi = next_tok->get_str ();\n+\t }\n+\t }\n+\t break;\n+\tdefault:\n+\t goto done;\n+\t}\n+ found_order.push_back (token_id);\n lexer.skip_token ();\n- unsafe_status = Unsafety::Unsafe;\n }\n+done:\n \n- if (lexer.peek_token ()->get_id () == EXTERN_KW)\n- {\n- lexer.skip_token ();\n- has_extern = true;\n+ if (!ensure_function_qualifier_order (locus, std::move (found_order)))\n+ return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);\n \n- // detect optional abi name\n- const_TokenPtr next_tok = lexer.peek_token ();\n- if (next_tok->get_id () == STRING_LITERAL)\n+ return std::unique_ptr<AST::FunctionQualifiers> (\n+ new AST::FunctionQualifiers (locus, default_status, async_status,\n+\t\t\t\t const_status, unsafe_status, has_extern,\n+\t\t\t\t std::move (abi)));\n+}\n+\n+template <typename ManagedTokenSource>\n+bool\n+Parser<ManagedTokenSource>::ensure_function_qualifier_order (\n+ location_t locus, std::vector<TokenId> found_order)\n+{\n+ // Check in order of default, const, async, unsafe, extern\n+ auto token_priority = [] (const TokenId id) {\n+ switch (id)\n+ {\n+ case IDENTIFIER: // \"default\"; the only \"weak\" keyword considered here\n+\treturn 1;\n+ case CONST:\n+\treturn 2;\n+ case ASYNC:\n+\treturn 3;\n+ case UNSAFE:\n+\treturn 4;\n+ case EXTERN_KW:\n+\treturn 5;\n+ default:\n+\trust_unreachable ();\n+ };\n+ };\n+\n+ size_t last_priority = 0;\n+ for (auto token_id : found_order)\n+ {\n+ const size_t priority = token_priority (token_id);\n+ if (priority <= last_priority)\n \t{\n-\t lexer.skip_token ();\n-\t abi = next_tok->get_str ();\n+\t auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {\n+\t std::ostringstream ss;\n+\n+\t for (auto id : token_ids)\n+\t {\n+\t\tif (ss.tellp () != 0)\n+\t\t ss << ' ';\n+\n+\t\tif (id == IDENTIFIER)\n+\t\t ss << Values::WeakKeywords::DEFAULT;\n+\t\telse\n+\t\t ss << token_id_keyword_string (id);\n+\t }\n+\n+\t return ss.str ();\n+\t };\n+\n+\t std::vector<TokenId> expected_order\n+\t = {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};\n+\n+\t // we only keep the qualifiers actually used in the offending code\n+\t std::vector<TokenId>::const_iterator token_id\n+\t = expected_order.cbegin ();\n+\t while (token_id != expected_order.cend ())\n+\t {\n+\t if (std::find (found_order.cbegin (), found_order.cend (),\n+\t\t\t *token_id)\n+\t\t == found_order.cend ())\n+\t\t{\n+\t\t token_id = expected_order.erase (token_id);\n+\t\t}\n+\t else\n+\t\t{\n+\t\t ++token_id;\n+\t\t}\n+\t }\n+\n+\t const std::string found_qualifiers = qualifiers_to_str (found_order);\n+\t const std::string expected_qualifiers\n+\t = qualifiers_to_str (expected_order);\n+\n+\t location_t error_locus\n+\t = make_location (locus, locus, lexer.peek_token ()->get_locus ());\n+\t Error error (\n+\t error_locus,\n+\t \"invalid order of function qualifiers; found %qs, expected %qs\",\n+\t found_qualifiers.c_str (), expected_qualifiers.c_str ());\n+\t add_error (std::move (error));\n+\n+\t return false;\n \t}\n+\n+ last_priority = priority;\n }\n \n- return AST::FunctionQualifiers (locus, default_status, async_status,\n- const_status, unsafe_status, has_extern,\n- std::move (abi));\n+ return true;\n }\n \n // Parses generic (lifetime or type) params inside angle brackets (optional).\n@@ -4276,7 +4391,9 @@ Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (\n {\n location_t locus = lexer.peek_token ()->get_locus ();\n // parse function or method qualifiers\n- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();\n+ auto qualifiers = parse_function_qualifiers ();\n+ if (!qualifiers)\n+ return nullptr;\n \n skip_token (FN_KW);\n \n@@ -4362,7 +4479,7 @@ Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (\n }\n \n return std::unique_ptr<AST::Function> (\n- new AST::Function (std::move (ident), std::move (qualifiers),\n+ new AST::Function (std::move (ident), std::move (*qualifiers.value ()),\n \t\t std::move (generic_params), std::move (function_params),\n \t\t std::move (return_type), std::move (where_clause),\n \t\t std::move (body), std::move (vis),\n@@ -4459,7 +4576,9 @@ Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (\n location_t locus = lexer.peek_token ()->get_locus ();\n \n // parse function or method qualifiers\n- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();\n+ auto qualifiers = parse_function_qualifiers ();\n+ if (!qualifiers)\n+ return nullptr;\n \n skip_token (FN_KW);\n \n@@ -4590,7 +4709,7 @@ Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (\n }\n \n return std::unique_ptr<AST::Function> (\n- new AST::Function (std::move (ident), std::move (qualifiers),\n+ new AST::Function (std::move (ident), std::move (*qualifiers.value ()),\n \t\t std::move (generic_params), std::move (function_params),\n \t\t std::move (return_type), std::move (where_clause),\n \t\t std::move (body), std::move (vis),\n@@ -6202,7 +6321,9 @@ Parser<ManagedTokenSource>::parse_bare_function_type (\n // TODO: pass in for lifetime location as param\n location_t best_try_locus = lexer.peek_token ()->get_locus ();\n \n- AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();\n+ auto qualifiers = parse_function_qualifiers ();\n+ if (!qualifiers)\n+ return nullptr;\n \n if (!skip_token (FN_KW))\n return nullptr;\n@@ -6285,11 +6406,10 @@ Parser<ManagedTokenSource>::parse_bare_function_type (\n \t}\n }\n \n- return std::unique_ptr<AST::BareFunctionType> (\n- new AST::BareFunctionType (std::move (for_lifetimes),\n-\t\t\t std::move (qualifiers), std::move (params),\n-\t\t\t is_variadic, std::move (variadic_attrs),\n-\t\t\t std::move (return_type), best_try_locus));\n+ return std::unique_ptr<AST::BareFunctionType> (new AST::BareFunctionType (\n+ std::move (for_lifetimes), std::move (*qualifiers.value ()),\n+ std::move (params), is_variadic, std::move (variadic_attrs),\n+ std::move (return_type), best_try_locus));\n }\n \n template <typename ManagedTokenSource>\ndiff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h\nindex 96b2d091fc7..8df9904766a 100644\n--- a/gcc/rust/parse/rust-parse.h\n+++ b/gcc/rust/parse/rust-parse.h\n@@ -364,7 +364,10 @@ private:\n std::unique_ptr<AST::Function> parse_function (AST::Visibility vis,\n \t\t\t\t\t\t AST::AttrVec outer_attrs,\n \t\t\t\t\t\t bool is_external = false);\n- AST::FunctionQualifiers parse_function_qualifiers ();\n+ tl::expected<std::unique_ptr<AST::FunctionQualifiers>, Parse::Error::Node>\n+ parse_function_qualifiers ();\n+ bool ensure_function_qualifier_order (location_t locus,\n+\t\t\t\t\tstd::vector<TokenId> found_order);\n std::vector<std::unique_ptr<AST::GenericParam>>\n parse_generic_params_in_angles ();\n template <typename EndTokenPred>\ndiff --git a/gcc/testsuite/rust/compile/func-qualifier-default.rs b/gcc/testsuite/rust/compile/func-qualifier-default.rs\nindex e2e600c4ba3..68be6b5aaa7 100644\n--- a/gcc/testsuite/rust/compile/func-qualifier-default.rs\n+++ b/gcc/testsuite/rust/compile/func-qualifier-default.rs\n@@ -12,7 +12,6 @@ impl<T> Trait for T {\n \n // `default` precedes all other qualifiers\n unsafe default fn unsafe_fn() {}\n- // { dg-error \"expecting .fn. but .identifier. found\" \"\" { target *-*-* } .-1 }\n- // { dg-error \"expecting ... but .fn. found\" \"\" { target *-*-* } .-2 }\n- // { dg-error \"failed to parse trait impl item in trait impl\" \"\" { target *-*-* } .-3 }\n+ // { dg-error \"invalid order of function qualifiers; found .unsafe default., expected .default unsafe.\" \"\" { target *-*-* } .-1 }\n+ // { dg-error \"failed to parse trait impl item in trait impl\" \"\" { target *-*-* } .-2 }\n }\ndiff --git a/gcc/testsuite/rust/compile/func-qualifier-order.rs b/gcc/testsuite/rust/compile/func-qualifier-order-1.rs\nsimilarity index 100%\nrename from gcc/testsuite/rust/compile/func-qualifier-order.rs\nrename to gcc/testsuite/rust/compile/func-qualifier-order-1.rs\ndiff --git a/gcc/testsuite/rust/compile/func-qualifier-order-2.rs b/gcc/testsuite/rust/compile/func-qualifier-order-2.rs\nnew file mode 100644\nindex 00000000000..7eebe7df930\n--- /dev/null\n+++ b/gcc/testsuite/rust/compile/func-qualifier-order-2.rs\n@@ -0,0 +1,6 @@\n+// { dg-additional-options \"-frust-edition=2018\" }\n+#![feature(no_core)]\n+#![no_core]\n+\n+async unsafe async fn duplicate_qualifier() {} // { dg-error \"encountered duplicate function qualifier .async.\" }\n+\ndiff --git a/gcc/testsuite/rust/compile/func-qualifier-order-3.rs b/gcc/testsuite/rust/compile/func-qualifier-order-3.rs\nnew file mode 100644\nindex 00000000000..6e7e69c02c1\n--- /dev/null\n+++ b/gcc/testsuite/rust/compile/func-qualifier-order-3.rs\n@@ -0,0 +1,14 @@\n+#![feature(min_specialization)]\n+#![feature(no_core)]\n+#![no_core]\n+\n+trait Dummy {\n+}\n+\n+// the purpose of this is to trigger the compiler message\n+// regarding the order of qualifiers\n+impl<T> Dummy for T {\n+ default async unsafe extern \"C\" const fn all_the_qualifiers() {}\n+ // { dg-error \"invalid order of function qualifiers; found .default async unsafe extern const., expected .default const async unsafe extern.\" \"\" { target *-*-* } .-1 }\n+ // { dg-error \"failed to parse trait impl item in trait impl\" \"\" { target *-*-* } .-2 }\n+}\n", "prefixes": [ "COMMITTED", "35/43" ] }