Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2218073/?format=api
{ "id": 2218073, "url": "http://patchwork.ozlabs.org/api/patches/2218073/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260331104730.3162-4-gerris.rs@gmail.com/", "project": { "id": 17, "url": "http://patchwork.ozlabs.org/api/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, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260331104730.3162-4-gerris.rs@gmail.com>", "list_archive_url": null, "date": "2026-03-31T10:47:29", "name": "[gccrs,COMMIT,4/5] gccrs: parse all function qualifiers before generating error", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "94f1982991d99a60617369f61e5e630cda623ecb", "submitter": { "id": 92092, "url": "http://patchwork.ozlabs.org/api/people/92092/?format=api", "name": null, "email": "gerris.rs@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260331104730.3162-4-gerris.rs@gmail.com/mbox/", "series": [ { "id": 498172, "url": "http://patchwork.ozlabs.org/api/series/498172/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=498172", "date": "2026-03-31T10:47:28", "name": "[gccrs,COMMIT,1/5] Add `default` as function qualifier.", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/498172/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2218073/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2218073/checks/", "tags": {}, "related": [], "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=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=aC/kqYnb;\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=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=aC/kqYnb", "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com", "sourceware.org; spf=pass smtp.mailfrom=gmail.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=209.85.210.53" ], "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 4flQ1X5lBXz1yGH\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 31 Mar 2026 21:52:32 +1100 (AEDT)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id C9E964BA23EA\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 31 Mar 2026 10:52:30 +0000 (GMT)", "from mail-ot1-f53.google.com (mail-ot1-f53.google.com\n [209.85.210.53])\n by sourceware.org (Postfix) with ESMTPS id BA39D4BA23EA\n for <gcc-patches@gcc.gnu.org>; Tue, 31 Mar 2026 10:47:37 +0000 (GMT)", "by mail-ot1-f53.google.com with SMTP id\n 46e09a7af769-7d75ed779bfso5545275a34.2\n for <gcc-patches@gcc.gnu.org>; Tue, 31 Mar 2026 03:47:37 -0700 (PDT)", "from\n runnervmrg6be.xariblbjf3ru1nzambwxmzlesd.gx.internal.cloudapp.net\n ([52.173.123.150]) by smtp.gmail.com with ESMTPSA id\n 46e09a7af769-7da0a819880sm7914900a34.22.2026.03.31.03.47.35\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 31 Mar 2026 03:47:36 -0700 (PDT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org C9E964BA23EA", "OpenDKIM Filter v2.11.0 sourceware.org BA39D4BA23EA" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org BA39D4BA23EA", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org BA39D4BA23EA", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774954057; cv=none;\n b=klQ7iNVPddGpq88hYpJFxFuEwQfkDjx8subvTW1IeiVtGAK+fQriHTW1XkJnRYc5h5RpoZKy9tsdAFdmkOFDtdOjbqPdM1D02RgZ32kSgr0X2mqKWgc3LjEYIjz5bm0Zqd4rgwzsKqIwnhjN2OgRNHMKgI49Y5CEHJ+bMo5gPqk=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1774954057; c=relaxed/simple;\n bh=/HhiaCrDltIHwBm9CS9XTxXcnZZcBaz5pLg9YSvTlfU=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=vBJF9jKrQzXEtgY1xHhZumPd4VN2vs0YFseZgT6lwOKLvQBNTZOZjo4zbShB9VctlflwwcSdPkRS+9L4Na25v2gE/nUjj1+aCQuFPQ6KzLLbW+YMkQooSpLtdy+7Sg9bqhfpFn5V52tvES9gbIfl/hNnA/39Sm4THhvdBbaBh1Y=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1774954057; x=1775558857; 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=ztfeTSd75riSRe+M6v2nx0VbKpLYbxROT4JNdqA4hpE=;\n b=aC/kqYnblSxr4j9+Zt7l8JIFuhzPrJkrnY+3gKC3CDbS5HzZzwW84+LirG+nMI7j4o\n tGJkuaWU1ljvPI1zg0Pj1XQMxOtEYHq5YuWBtQkPWIgRtUE2YJV/w/Er67Gqy13s9wid\n bprf1B58Ab1U/DFFEmWpJm/snSjCRYZP7+qVnWrcsAKuIXgIFscM0en9XT3tbqJCPkZe\n 3ehALA9ETLFwWyyokPm4twdJ8ovpVKFzXlUBGoKmnwqRT9Vf9sUVLw4mNQ0mBfGdTGY6\n J8D0L8sujXLeZU05BCRbZYNgsdEXcNPS0fPg1nsvHYvvslKC9CGcMm5LXGwZo92LfdVB\n l2iQ==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1774954057; x=1775558857;\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=ztfeTSd75riSRe+M6v2nx0VbKpLYbxROT4JNdqA4hpE=;\n b=gTpl44IMIhNoj8X5V3A0nXg2lelcJhL3ag8y7aLuve0vtCkA3pa7bRqhQ9CBa7wTj/\n WLauc2Ktw+ytZRmT2b7PAz+vHtmx30YQOpWhJuEApNKjWRHbIeXr9ZX3lsWkv09GGPbL\n c03SUpjsP1b6FAHPrdP5yhYz9caYCBINwfN7hUqnvaY0u+zMWzXGMGQreBSPKPlC5Gil\n pUvD5gljRGUhM+4uf/YpTpn0WudAS5ik1CoIBEQrA9m1OCzzsW0Enc8y6+wlpXOYAOLI\n gHaPv7FT2TagVXt4sMOXzal7hjRKTD8M95isw2ne5I8GUPjQLIM36ntf1GcV46U5jpfU\n 64tw==", "X-Gm-Message-State": "AOJu0YyU/WBRZuVN4XbA6AlUOvI97jqO01Chjgnnf14gm7dBcwJoT2Kx\n FqcH6o8pJLvke/5+0mOFZya1lN/RfQ7bOsNh0+breWfd64MA/24pvJbqz6h2KgBp", "X-Gm-Gg": "ATEYQzz42r441aZXI9ZyK00LoGn/ezmdEMj+tzgV1e/elYybZ5jzOvj/P0TwSuh8foi\n Lm1gS+jAyHzFXodq/dO94+WowFejsFBSOCPYCQiiSGuyRtj1FK6xS9TfZ/dVXz8C1CJ4a7dUXI7\n 5KiqOQC5iO709L+WC2o6TFUoxXzfzAFHgPQg4CC6U7NYpldKjLL+gVkxPDR2zLzKvCD3can8b6N\n U5SF/Tzr3Ax5ap2x2AbjpsCV3lWcQ9fvNPdqxaQfO4tl/TUkr1JXU0RBP/KbaP0z09+4qcRB7LQ\n /UZVgaLgho83uD6P/6uTc+ydkc1lhtggkNUpFubF4a03h1v8m60ibteEFwAC3Su4Eve0jhLrjTK\n Q5Bg2DawBR6fz/UXbY2rwtTe5ocjCpQ05/RBmtWrjrVDbRWAK7WbRDqW6y+bjfkR+TwAzNMB22M\n Igy+jAsJy5tqMC5Z3zGWP+bom1kXk0xrMiM3cODRolycuc5cM6Gn4XlNgXs1enzC8XkGYjDDnok\n 9FGBRFSzbnv5bP650f+isw=", "X-Received": "by 2002:a05:6830:3897:b0:7d7:dcb1:1e6c with SMTP id\n 46e09a7af769-7d9fad9a2e3mr9438104a34.7.1774954056770;\n Tue, 31 Mar 2026 03:47:36 -0700 (PDT)", "From": "gerris.rs@gmail.com", "To": "gcc-patches@gcc.gnu.org,\n\tgcc-rust@gcc.gnu.org", "Cc": "pierre-emmanuel.patry@embecosm.com, arthur.cohen@embecosm.com,\n dkm+gccrs@kataplop.net, Philipp Gesang <phg@phi-gamma.net>", "Subject": "[gccrs COMMIT 4/5] gccrs: parse all function qualifiers before\n generating error", "Date": "Tue, 31 Mar 2026 10:47:29 +0000", "Message-ID": "<20260331104730.3162-4-gerris.rs@gmail.com>", "X-Mailer": "git-send-email 2.43.0", "In-Reply-To": "<20260331104730.3162-1-gerris.rs@gmail.com>", "References": "<20260331104730.3162-1-gerris.rs@gmail.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---\nThis change was merged into the gccrs repository and is posted here for\nupstream visibility and potential drive-by review, as requested by GCC\nrelease managers.\nEach commit email contains a link to its details on github from where you can\nfind the Pull-Request and associated discussions.\n\n\nCommit on github: https://github.com/Rust-GCC/gccrs/commit/2e82591531334d52facca744ec9ad7350a776e3a\n\nThe commit has been mentioned in the following pull-request(s):\n - https://github.com/Rust-GCC/gccrs/pull/4463\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 8820e3555..fc9f45a8e 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 96b2d091f..8df990476 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 e2e600c4b..68be6b5aa 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 000000000..7eebe7df9\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 000000000..6e7e69c02\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": [ "gccrs", "COMMIT", "4/5" ] }