get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.2/patches/2223300/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2223300,
    "url": "http://patchwork.ozlabs.org/api/1.2/patches/2223300/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260414231905.191063-36-arthur.cohen@embecosm.com/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/1.2/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": "<20260414231905.191063-36-arthur.cohen@embecosm.com>",
    "list_archive_url": null,
    "date": "2026-04-14T23:18:57",
    "name": "[COMMITTED,36/43] gccrs: refactor function qualifier parsing",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "b00b8af12d7b43009444d14a8b9c912aac5672b2",
    "submitter": {
        "id": 83476,
        "url": "http://patchwork.ozlabs.org/api/1.2/people/83476/?format=api",
        "name": "Arthur Cohen",
        "email": "arthur.cohen@embecosm.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260414231905.191063-36-arthur.cohen@embecosm.com/mbox/",
    "series": [
        {
            "id": 499900,
            "url": "http://patchwork.ozlabs.org/api/1.2/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/2223300/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2223300/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=embecosm.com header.i=@embecosm.com header.a=rsa-sha256\n header.s=google header.b=HEBkUJRo;\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=HEBkUJRo",
            "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.52"
        ],
        "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 4fwL8W62Ljz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 15 Apr 2026 09:29:31 +1000 (AEST)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 138BF4BA23F4\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 14 Apr 2026 23:29:30 +0000 (GMT)",
            "from mail-wm1-f52.google.com (mail-wm1-f52.google.com\n [209.85.128.52])\n by sourceware.org (Postfix) with ESMTPS id CE0B34BA23C2\n for <gcc-patches@gcc.gnu.org>; Tue, 14 Apr 2026 23:15:06 +0000 (GMT)",
            "by mail-wm1-f52.google.com with SMTP id\n 5b1f17b1804b1-483487335c2so67915415e9.2\n for <gcc-patches@gcc.gnu.org>; Tue, 14 Apr 2026 16:15:06 -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.03\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 14 Apr 2026 16:15:03 -0700 (PDT)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org 138BF4BA23F4",
            "OpenDKIM Filter v2.11.0 sourceware.org CE0B34BA23C2"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org CE0B34BA23C2",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org CE0B34BA23C2",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776208507; cv=none;\n b=LOe4NjiuBrGlE2EQStjeUWwPKmsZcQdGGgFbGZfFGVYEzf50lx1p6CpPz4IJQImQdpt0LcDDHcLei7NvvTgSfEav2frr4CrSK1ifzwxVl4CL1L3BAUy5WkL3VJvvodEnMSD2Qr1OZTUoT3GYcBEJsUuoLV2Z2ohrO1wCIAJUrF0=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776208507; c=relaxed/simple;\n bh=Z/lqOfohV4qOAN8NbDuXYIhDeDliU5lKQCbcgZpoX4g=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=RlQXTlSyBhbhC8NuReJgpdtOX9Hd53V5MEyXXMkXbcLIJ6ydivrbVLeERUta87ZAQOa1pqvfD+8i+RnmESG9Brq4qkJ3gzSrVrif0WJ8UVcl+/WfIk+5CYWMHAmwBBImtSfiYjo1poo/ou4sVB9wE5/5SogMPJRXCOcz5wV+FZA=",
        "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=1776208505; x=1776813305; 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=V/8YcMqOf2uaCnwHMAhzAwRBeN/FDPKEv7rv/FoIAmQ=;\n b=HEBkUJRoy2s5lofUf8QLki0wCokiSLcja8GVpZaarkjwPoOYamGs3W97A+YqceY2a4\n AA3SgzNwy5TsyGx3PoS0lQqxZptObMY0+/8RIbbcC2h4f2lpo8ciWSbi8IfYFYK68cYN\n E4KVowYHSS5ruVTGM5z8s/wlp4fcuFHXVOks3Z8d4Jcg/n550zV5tQ7jpSdFvtA09Qem\n evhsDg4zJxbn1uNR0Z6ynlD/SvAzfT4japhj/qC5bj+gsMGbzUkPM6rzW79qbFIzBFxb\n FqjyKdMzh5juxRBNd1H81TXcea6G+N+tmtmfp0dK6AVIzcEFLrQl9ENZWFPgwFgIPfNl\n D7Dw==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776208505; x=1776813305;\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=V/8YcMqOf2uaCnwHMAhzAwRBeN/FDPKEv7rv/FoIAmQ=;\n b=D3xvh32hXEjX3bwLJEg63crtmXHzezbqs78vhf5ttQVQIr+BAqSuS2yf8lqf/pPnXJ\n NOjG/LH9xaXJaPF0s3nPRsG4ZFREG1NbkDTwBXCz5S8vyQfh/drsh7CWNpfjysGO3Uv2\n bH6RN45m/q3EjSnQp+Sdcckn4WHywB7pbRfkEC5voEwOf8qkG9JT1/81/saGvPNQHGtf\n 49WEvBXHrGXhU7YUHm3/pbw/vd4C7KzSaeJhyaefQXpQaXdi6w1I86CYXGdj8ScWpqOp\n HVR5Smbk/ANRqUjI/e+fh2nbzLIWeqTkVeN812+5mJx6AJeDLiqbYB9TaRXghZxzBN4m\n Q9Cg==",
        "X-Gm-Message-State": "AOJu0YxQX8/NIOuFJQZ54NRcbkrSta+cnw98tJBXl63YFJiUbBu5J3fg\n m+wUnzWilh995lsYeF6zfnT21kdG1nn6yJcdi7MRU1Tl9u/ukzaWisvv92A0zPxjeBzie6Fixjh\n wbY8IPg==",
        "X-Gm-Gg": "AeBDiescLUJODVyFdqM2NJUAm6YJkbgQUmvLH9ceviuUqs/w3FeRdPfpUt6S1jBsHof\n uQukSwLKZBcpzSxoLbrCj1aXfw8/mdFJTNYPMuMpf8gCAziYbVnV/5tfmTi6KU1kbu8V517mEuC\n cDh8nuX6fG7RbKYsekwHsyzBdyET8GY5TG7yB1tuWlcRGj2IfZo37Y9KjQnPr0ayIQsF6PvOKl4\n lREa85ykaon4dJjmbSQ4DG7wasKQKaFBD0D18JDxYsM3yiGrk7S4sT1i8KRJ4/XLD9RyvLjg0rM\n 3F2rNbu6WjD+KY5+Til9OYTKC75PXRC9PfUBeCzsGl7qVNrHzXjoS7qlrmZOQ1AcbGbXFJUmbZW\n lL6ylzbXVsOYLauTV3MGU7tI36wNlF2bdtetc0mwpVkUegYIpFBfjdqMV0ZnGQq93OiO8ijx8Ra\n Q2OGpBIIpc7LlT7sdThRAGLRmi6RwAkH4gRY55uvtLe9QlULwZjZFHmeAdqCdLsw3FPnAplwYhu\n k+Kow==",
        "X-Received": "by 2002:a05:600c:8284:b0:488:d228:a133 with SMTP id\n 5b1f17b1804b1-488d683d280mr252479375e9.14.1776208505486;\n Tue, 14 Apr 2026 16:15:05 -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 36/43] gccrs: refactor function qualifier parsing",
        "Date": "Wed, 15 Apr 2026 01:18:57 +0200",
        "Message-ID": "<20260414231905.191063-36-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\ngcc/rust/ChangeLog\n\t* parse/rust-parse-impl.hxx: Refactor qualifier parsing\n\t* parse/rust-parse.h: (parse_function_qualifiers) Likewise\n\nSigned-off-by: Philipp Gesang <phg@phi-gamma.net>\n---\n gcc/rust/parse/rust-parse-impl.hxx | 236 ++++++++++++++++++-----------\n gcc/rust/parse/rust-parse.h        |  16 +-\n 2 files changed, 159 insertions(+), 93 deletions(-)",
    "diff": "diff --git a/gcc/rust/parse/rust-parse-impl.hxx b/gcc/rust/parse/rust-parse-impl.hxx\nindex fc9f45a8e5c..97bd5b15e1f 100644\n--- a/gcc/rust/parse/rust-parse-impl.hxx\n+++ b/gcc/rust/parse/rust-parse-impl.hxx\n@@ -1638,7 +1638,7 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,\n     }\n \n   return std::unique_ptr<AST::Function> (new AST::Function (\n-    std::move (function_name), std::move (*qualifiers.value ()),\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@@ -1646,93 +1646,146 @@ Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,\n \n // Parses function or method qualifiers (i.e. const, unsafe, and extern).\n template <typename ManagedTokenSource>\n-tl::expected<std::unique_ptr<AST::FunctionQualifiers>, Parse::Error::Node>\n+tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n Parser<ManagedTokenSource>::parse_function_qualifiers ()\n+{\n+  location_t locus = lexer.peek_token ()->get_locus ();\n+\n+  auto parsed = parse_function_qualifiers_raw (locus);\n+  if (!parsed)\n+    return tl::unexpected<Parse::Error::Node> (parsed.error ());\n+\n+  return function_qualifiers_from_keywords (locus, std::move (parsed->first),\n+\t\t\t\t\t    std::move (parsed->second));\n+}\n+\n+// Take the list of parsed function qualifiers and convert it to\n+// the corrresponding flags to pass to the AST item constructor.\n+//\n+// This assumes ``keywords`` contains only those tokens that\n+// map to qualifiers.\n+template <typename ManagedTokenSource>\n+tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n+Parser<ManagedTokenSource>::function_qualifiers_from_keywords (\n+  location_t locus, const std::vector<TokenId> keywords, std::string abi)\n {\n   Default default_status = Default::No;\n   Async async_status = Async::No;\n   Const const_status = Const::No;\n   Unsafety unsafe_status = Unsafety::Normal;\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+  for (auto qualifier : keywords)\n+    {\n+      switch (qualifier)\n+\t{\n+\tcase IDENTIFIER:\n+\t  // only \"default\" is valid in this context\n+\t  default_status = Default::Yes;\n+\t  continue;\n+\tcase CONST:\n+\t  const_status = Const::Yes;\n+\t  continue;\n+\tcase ASYNC:\n+\t  async_status = Async::Yes;\n+\t  continue;\n+\tcase UNSAFE:\n+\t  unsafe_status = Unsafety::Unsafe;\n+\t  continue;\n+\tcase EXTERN_KW:\n+\t  has_extern = true;\n+\t  continue;\n+\tdefault:\n+\t  // non-qualifier token in input\n+\t  rust_unreachable ();\n+\t}\n+    }\n \n-  const_TokenPtr t;\n-  location_t locus = lexer.peek_token ()->get_locus ();\n+  return 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+// this consumes as many function qualifier tokens while ensuring\n+// uniqueness.\n+template <typename ManagedTokenSource>\n+tl::expected<std::pair<std::vector<TokenId>, std::string>, Parse::Error::Node>\n+Parser<ManagedTokenSource>::parse_function_qualifiers_raw (location_t locus)\n+{\n+  std::vector<TokenId> found_order;\n+  std::string abi;\n \n   // this will terminate on duplicates or the first non-qualifier token\n   while (true)\n     {\n-      const TokenId token_id = lexer.peek_token ()->get_id ();\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-\t  return tl::unexpected<Parse::Error::Node> (\n-\t    Parse::Error::Node::MALFORMED);\n-\t}\n+      auto token = lexer.peek_token ();\n+      const TokenId token_id = token->get_id ();\n+      location_t locus = lexer.peek_token ()->get_locus ();\n \n       switch (token_id)\n \t{\n \tcase IDENTIFIER:\n-\t  if (lexer.peek_token ()->get_str () != Values::WeakKeywords::DEFAULT)\n+\t  if (token->get_str () != Values::WeakKeywords::DEFAULT)\n \t    {\n-\t      // only \"default\" is valid in this context\n+\t      // only \"default\" is valid in this context, so this must\n+\t      // be a non-qualifier keyword\n \t      goto done;\n \t    }\n-\t  default_status = Default::Yes;\n-\t  break;\n+\t  // fallthrough\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  found_order.push_back (token_id);\n+\t  lexer.skip_token ();\n \t  break;\n \tcase EXTERN_KW:\n \t  {\n-\t    has_extern = true;\n-\t    // detect optional abi name\n+\t    found_order.push_back (token_id);\n \t    lexer.skip_token ();\n+\n+\t    // detect optional abi name\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\tlexer.skip_token ();\n \t      }\n \t  }\n \t  break;\n \tdefault:\n+\t  // non-qualifier keyword\n \t  goto done;\n \t}\n-      found_order.push_back (token_id);\n-      lexer.skip_token ();\n+\n+      if (std::count (found_order.cbegin (), found_order.cend (), token_id) > 1)\n+\t{\n+\t  // qualifiers mustn't appear twice\n+\t  Error error (locus, \"encountered duplicate function qualifier %qs\",\n+\t\t       token->get_token_description ());\n+\t  add_error (std::move (error));\n+\n+\t  return tl::unexpected<Parse::Error::Node> (\n+\t    Parse::Error::Node::MALFORMED);\n+\t}\n     }\n done:\n \n-  if (!ensure_function_qualifier_order (locus, std::move (found_order)))\n+  if (!ensure_function_qualifier_order (locus, found_order))\n     return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED);\n \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+  return make_pair (found_order, abi);\n }\n \n+// Validate the order of the list of function qualifiers; this assumes that\n+// ``found_order`` consists only of function qualifier tokens.\n+//\n+// If the order is illegal, the generated error message gives both the wrong\n+// order as found in the source and the correct order according to Rust syntax\n+// rules.\n template <typename ManagedTokenSource>\n bool\n Parser<ManagedTokenSource>::ensure_function_qualifier_order (\n-  location_t locus, std::vector<TokenId> found_order)\n+  location_t locus, const std::vector<TokenId> &found_order)\n {\n   // Check in order of default, const, async, unsafe, extern\n   auto token_priority = [] (const TokenId id) {\n@@ -1759,62 +1812,65 @@ Parser<ManagedTokenSource>::ensure_function_qualifier_order (\n       const size_t priority = token_priority (token_id);\n       if (priority <= last_priority)\n \t{\n-\t  auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {\n-\t    std::ostringstream ss;\n+\t  emit_function_qualifier_order_error_msg (locus, found_order);\n+\t  return false;\n+\t}\n \n-\t    for (auto id : token_ids)\n-\t      {\n-\t\tif (ss.tellp () != 0)\n-\t\t  ss << ' ';\n+      last_priority = priority;\n+    }\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+  return true;\n+}\n \n-\t    return ss.str ();\n-\t  };\n+template <typename ManagedTokenSource>\n+void\n+Parser<ManagedTokenSource>::emit_function_qualifier_order_error_msg (\n+  location_t locus, const std::vector<TokenId> &found_order)\n+{\n+  std::vector<TokenId> expected_order\n+    = {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};\n \n-\t  std::vector<TokenId> expected_order\n-\t    = {IDENTIFIER, CONST, ASYNC, UNSAFE, EXTERN_KW};\n+  // we only keep the qualifiers actually used in the offending code\n+  std::vector<TokenId>::iterator token_id = expected_order.begin ();\n+  while (token_id != expected_order.end ())\n+    {\n+      if (std::find (found_order.cbegin (), found_order.cend (), *token_id)\n+\t  == found_order.cend ())\n+\t{\n+\t  token_id = expected_order.erase (token_id);\n+\t}\n+      else\n+\t{\n+\t  ++token_id;\n+\t}\n+    }\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+  auto qualifiers_to_str = [] (const std::vector<TokenId> &token_ids) {\n+    std::ostringstream ss;\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+    for (auto id : token_ids)\n+      {\n+\tif (ss.tellp () != 0)\n+\t  ss << ' ';\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+\tif (id == IDENTIFIER)\n+\t  ss << Values::WeakKeywords::DEFAULT;\n+\telse\n+\t  ss << token_id_keyword_string (id);\n+      }\n \n-\t  return false;\n-\t}\n+    return ss.str ();\n+  };\n \n-      last_priority = priority;\n-    }\n+  const std::string found_qualifiers = qualifiers_to_str (found_order);\n+  const std::string expected_qualifiers = qualifiers_to_str (expected_order);\n \n-  return true;\n+  location_t error_locus\n+    = make_location (locus, locus, lexer.peek_token ()->get_locus ());\n+  Error error (error_locus,\n+\t       \"invalid order of function qualifiers; found %qs, expected %qs\",\n+\t       found_qualifiers.c_str (), expected_qualifiers.c_str ());\n+  add_error (std::move (error));\n }\n \n // Parses generic (lifetime or type) params inside angle brackets (optional).\n@@ -4479,7 +4535,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.value ()),\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@@ -4709,7 +4765,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.value ()),\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@@ -6407,7 +6463,7 @@ Parser<ManagedTokenSource>::parse_bare_function_type (\n     }\n \n   return std::unique_ptr<AST::BareFunctionType> (new AST::BareFunctionType (\n-    std::move (for_lifetimes), std::move (*qualifiers.value ()),\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 }\ndiff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h\nindex 8df9904766a..dac39da3660 100644\n--- a/gcc/rust/parse/rust-parse.h\n+++ b/gcc/rust/parse/rust-parse.h\n@@ -364,10 +364,20 @@ 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-  tl::expected<std::unique_ptr<AST::FunctionQualifiers>, Parse::Error::Node>\n+  tl::expected<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+  tl::expected<std::pair<std::vector<TokenId>, std::string>, Parse::Error::Node>\n+  parse_function_qualifiers_raw (location_t locus);\n+  bool\n+  ensure_function_qualifier_order (location_t locus,\n+\t\t\t\t   const std::vector<TokenId> &found_order);\n+  tl::expected<AST::FunctionQualifiers, Parse::Error::Node>\n+  function_qualifiers_from_keywords (location_t locus,\n+\t\t\t\t     std::vector<TokenId> keywords,\n+\t\t\t\t     std::string abi);\n+  void emit_function_qualifier_order_error_msg (\n+    location_t locus, const std::vector<TokenId> &found_order);\n+\n   std::vector<std::unique_ptr<AST::GenericParam>>\n   parse_generic_params_in_angles ();\n   template <typename EndTokenPred>\n",
    "prefixes": [
        "COMMITTED",
        "36/43"
    ]
}