{"id":2233048,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2233048/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/patch/20260505151030.1749548-5-waffl3x@baylibre.com/","project":{"id":17,"url":"http://patchwork.ozlabs.org/api/1.2/projects/17/?format=json","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":"<20260505151030.1749548-5-waffl3x@baylibre.com>","list_archive_url":null,"date":"2026-05-05T15:01:57","name":"[04/12] OpenMP/C++: Add support for 'omp allocate' directive","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"466aeef02600dac235f5a1b009da9a9f940425fc","submitter":{"id":90070,"url":"http://patchwork.ozlabs.org/api/1.2/people/90070/?format=json","name":"Waffl3x","email":"waffl3x@baylibre.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/gcc/patch/20260505151030.1749548-5-waffl3x@baylibre.com/mbox/","series":[{"id":502853,"url":"http://patchwork.ozlabs.org/api/1.2/series/502853/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/list/?series=502853","date":"2026-05-05T15:01:54","name":"OpenMP/C++: 'allocate' directive","version":1,"mbox":"http://patchwork.ozlabs.org/series/502853/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2233048/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2233048/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=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=qCxZ55fG;\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=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=qCxZ55fG","sourceware.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com","sourceware.org; spf=pass smtp.mailfrom=baylibre.com","server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::432"],"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 4g92D9497tz1yJx\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 06 May 2026 01:16:40 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 642694BA23DD\n\tfor <incoming@patchwork.ozlabs.org>; Tue,  5 May 2026 15:16:38 +0000 (GMT)","from mail-pf1-x432.google.com (mail-pf1-x432.google.com\n [IPv6:2607:f8b0:4864:20::432])\n by sourceware.org (Postfix) with ESMTPS id 1DD054BA2E31\n for <gcc-patches@gcc.gnu.org>; Tue,  5 May 2026 15:10:42 +0000 (GMT)","by mail-pf1-x432.google.com with SMTP id\n d2e1a72fcca58-8368a5d79d3so156665b3a.0\n for <gcc-patches@gcc.gnu.org>; Tue, 05 May 2026 08:10:42 -0700 (PDT)","from waffl3x-prestige.lan ([2001:56a:f98a:b800:1f67:ce08:3cbd:86b8])\n by smtp.gmail.com with ESMTPSA id\n d2e1a72fcca58-83965645140sm2674956b3a.12.2026.05.05.08.10.38\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 05 May 2026 08:10:39 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org 642694BA23DD","OpenDKIM Filter v2.11.0 sourceware.org 1DD054BA2E31"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 1DD054BA2E31","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 1DD054BA2E31","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777993842; cv=none;\n b=CWiQPJCpaRYrJbaWj1yACVHsdFMR/va5hfNxUm65uKpWFMZVXLD4MOS2L5hUibkSulmuDFfd23spzpmiU1McdyuK2yLP2+W3FrsgansdrF+jiBHi1tDUyDkkq9T4umgD4NSZ2MtFPNqzggime2RFp5sZ3iNmtHaJfv+nw0HBw3A=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777993842; c=relaxed/simple;\n bh=M4/OipUbCLjljJfiWH4M8jgtZFBYFogsk2ILu3j+thI=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=Bcug/l3D8wr15WTvuE+hfaYYB9BqO4GEcpZG1ldM3byJ6UovwkLuKhvj33htzr6bKgeu0LjfwMs/7eb+ZpdZ2aY0WlUm6cqXxQhAhWsNQTprmTbRO/drxpqruxMJya1bRgZlIHgPnpUTvbFapZVLEHxR62OfOSDOHnEH27W7g2k=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=baylibre-com.20251104.gappssmtp.com; s=20251104; t=1777993841;\n x=1778598641;\n 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=g3QPF7VO/B48TVRw61bNkeBheXLFivzu04n8gS7biV4=;\n b=qCxZ55fGjt5pRIP7cY5G1pL7TOeLlE+ObadpvLSOccmFHqO8yF5l6s8dkuzdy72ZXu\n jjfrxhrNb6nEq82Hcec0kySXajrE65aPC+zCzDA9wZ710aYqouT+rpcIBh6KXSB6O/mN\n luShqt8hfkJ+m69I1At3LRcA6uh2M0gAroJczghDI3qywaSeTPAD5QSYpu7wzMRdzZTq\n zSfYOduysgttQdV3ixbh4p+VA+x05SnfhhXxEjUFOoaQwkIhmRGUKgGxCSKFa2nTc817\n o3zg6PmOp5wKe5Od7+dTX0mwr2FH1qmesDCgV/PsepHudK3ABnlvcdMDJiBYy3dJQWrU\n e7MA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777993841; x=1778598641;\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=g3QPF7VO/B48TVRw61bNkeBheXLFivzu04n8gS7biV4=;\n b=HoUSoquoOi9QRpiLkQrPbNRLyurMGytFELDMGdga0Tm8arz70xJ60p1w/peVxe8Zwz\n uLMqOJeYpPrq6WaXkTmdOfui2CPeP61TThoO5oI0PMGS5vvs2XorTRUatLPD5+e0GSOQ\n cAAz4H+BzuMG0HsLvkpnUxd0smrxNijHP6g3dDXwtq/ovNRynwYonS8knSkiX9XlTZX8\n Q5Ee3zXDvnw53U/miAwmD7W+sTc1kzqjBT/Fp2O7cbThtFewqC+oa/jvp+1qDe4pcngk\n ueIbrDIxC75lLF3bsN+COAVfiD2fGe5X0f7HNyM07gjC99HWks1torl03kN7COQAC4Mx\n 4+4A==","X-Gm-Message-State":"AOJu0YxafyEume2yxRtWYs04H1+rcpvZskYmbUBd5u+JPs1pYcnrRZG4\n vwqCTU+4FEaFTXJKTiI6Qp1iiLW1K3owGazZt911xoC7bIxBubqFL6qwHWaAIxXlG9BwvlMWcVx\n Sr1dZ","X-Gm-Gg":"AeBDievl5czXQ6JTLged5m69Amr3P7QWF7TWleiWkf71K1utnogLldGii+OkkE7CYus\n SQDoS65lL4GCe16fXPJOFaypjsEGBH4hXrae5blvT+UDuoamQOdSMf55UKQqgVON8pF/slDLizd\n OrtLF6ONBE34iMSXT03A0h113ThHIvy2Tly5EzMsOs0sxkV9Iz1RYCyBL05a4G0pW1CM7m3uzpA\n X7qZRamkJs63GNBZ9hoXRDaealkl0qHV1A3+rO0nKb5JRdlpGaWg+FKrshxUl8QAusZA5qo8yq+\n Cf+Msclo+G5oj23MO9HjC9P1mRHQbzL4Jlsn3Jn74p/eW7O5rjrxTUEEScuSc1xjCc52/IeTbX+\n CLizQoxc1BqbNRFpIDXBPIJ4fu0bU/sJbSTEVHjjmTORsnn6MFq+7cWgFi7rcDSzwixW+Dvor85\n cpuDzY0ifY7SItxt0CZcdUhuWfB3gCf8wS3dXUMoYISicwq47MHA==","X-Received":"by 2002:a05:6a00:760d:b0:835:36f2:7332 with SMTP id\n d2e1a72fcca58-83536f27ff9mr5052063b3a.2.1777993839821;\n Tue, 05 May 2026 08:10:39 -0700 (PDT)","From":"Waffl3x <waffl3x@baylibre.com>","To":"gcc-patches@gcc.gnu.org","Cc":"Waffl3x <waffl3x@baylibre.com>","Subject":"[PATCH 04/12] OpenMP/C++: Add support for 'omp allocate' directive","Date":"Tue,  5 May 2026 09:01:57 -0600","Message-ID":"<20260505151030.1749548-5-waffl3x@baylibre.com>","X-Mailer":"git-send-email 2.54.0","In-Reply-To":"<20260505151030.1749548-1-waffl3x@baylibre.com>","References":"\n <CAH+W3Ppbho4pj6W-rWk4mMssrttOjt7aNco-oWW5Sw5f5Yx2GA@mail.gmail.com>\n <20260505151030.1749548-1-waffl3x@baylibre.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":"This patch adds support for the 'omp allocate' directive in C++. In parity\nwith the C front end, each variable found in an allocate directive has an\n'omp allocate' attribute added to it to mark it for transformation during\nlowering. This patch makes minimal modifications to gimplification aside\nfrom clearing C++ specific diagnostic information. This attribute is added\nto VAR_DECL nodes during parsing to ensure correct diagnostics. Due to the\nnature of this feature, the 'omp allocate' attribute is parsed after each\nVAR_DECL, thus care must be taken to preserve ordering with regards to\ndependent attributes and non-dependent attributes. This is fairly brittle,\nbut appears to be a better solution than marking the attribute itself\ndependent to avoid copying the attribute each time the VAR_DECL is\nsubstituted into.\n\nA new tree code, OMP_ALLOCATE, is added for use during substitution;\nit should not persist after a template is instantiated. The clauses\nattached to the allocate directive are stored in this node. Implementing\nsubstitution this way allows it to be done once per directive, which\nadditionally allows us to emit less repetitive and more informative\ndiagnostics.\n\nThis implementation diverges from the C front end in a few ways due to\ndiscussions with the OpenMP committee clarifying intended behavior. Where\nthe C front end requires the exact type in a clause, this patch allows\nimplicit conversions. Clauses are morally function calls and its arguments\nshould be treated as if they are binding to a parameter of the clause.\n\nThis patch, given it is meant to introduce support, defers diagnostics\nof clause arguments in templates until instantiation. Many cases likely\nwork simply by checking for value dependence of the clause argument instead\nof merely checking processing_template_decl, however, there were\ndifficulties performing implicit conversions before instantiation. Due to\nthese issues and for simplicity, such enhancements have been deferred to a\nlater patch.\n\nFinalization of each VAR_DECL's 'omp allocate' attribute is always done\nduring instantiation. While it is technically possible to do so earlier,\nthe conditions are slightly more complex than simply checking if the clause\narguments are non-dependent. In particular, the expr in an allocator clause\nargument that is non-constant must not have local variables as those nodes\nare not stable during substitution, even if they are non-dependent.\nThis enhancement is relatively minor and thus not a priority.\n\nAs an aside, the allocator clause expr is considered manifestly constant\nevaluated if there is a static local variable in the allocate directive's\narguments. While somewhat unintuitive, as it changes the semantics for each\nvariable, I believe that this best reflects the specification.\n\nStatic member variables in class templates have not been tested, and\nwere considered outside of the scope of this patch.\n\ngcc/cp/ChangeLog:\n\n\t* constexpr.cc (cxx_eval_constant_expression)\n\t<case OMP_ALLOCATE>: New.\n\t(potential_constant_expression_1) <case OMP_ALLOCATE>: New.\n\t* cp-tree.def (OMP_ALLOCATE): New tree code.\n\t* cp-tree.h (OMP_ALLOCATE_VARS): Define.\n\t(OMP_ALLOCATE_ALLOCATOR): Define.\n\t(OMP_ALLOCATE_ALIGN): Define.\n\t(finish_omp_allocate): New declaration.\n\t* parser.cc (cp_parser_omp_allocate): Rework, remove sorry, call\n\tfinish_omp_allocate.\n\t* pt.cc (tsubst_stmt) <case OMP_ALLOCATE>: New.\n\t* semantics.cc (finish_omp_allocate): New definition.\n\ngcc/ChangeLog:\n\n\t* gimplify.cc (gimplify_bind_expr): Add minor C++ specific checks.\n\nlibgomp/ChangeLog:\n\n\t* testsuite/libgomp.c/allocate-4.c: Move to...\n\t* testsuite/libgomp.c-c++-common/allocate-4.c: ...here.\n\t* testsuite/libgomp.c/allocate-5.c: Move to...\n\t* testsuite/libgomp.c-c++-common/allocate-5.c: ...here.\n\t* testsuite/libgomp.c/allocate-6.c: Move to...\n\t* testsuite/libgomp.c-c++-common/allocate-6.c: ...here.\n\t* testsuite/libgomp.c/allocate-7.c: Move to...\n\t* testsuite/libgomp.c-c++-common/allocate-7.c: ...here.\n\t* testsuite/libgomp.c++/allocate-2.C: New test.\n\ngcc/testsuite/ChangeLog:\n\n\t* c-c++-common/gomp/allocate-5.c: Modify for C++, remove sorry.\n\t* c-c++-common/gomp/allocate-9.c: Use header, add dg-line modify\n\tfor C++, remove sorry.\n\t* c-c++-common/gomp/allocate-10.c: Enable for C++, add dg-bogus.\n\t* c-c++-common/gomp/allocate-11.c: Enable and xfail for C++,\n\tadd -Wno-switch-unreachable.\n\t* c-c++-common/gomp/allocate-12.c: Enable for C++, add new xfailing\n\tcases, xfail C++ cases.\n\t* c-c++-common/gomp/allocate-14.c: Enable for C++.\n\t* c-c++-common/gomp/allocate-15.c: Enable for C++.\n\t* c-c++-common/gomp/allocate-16.c: Minor changes, enable for C++,\n\txfail C++ cases.\n\t* c-c++-common/gomp/allocate-17.c: Remove sorry.\n\t* c-c++-common/gomp/allocate-18.c: Use header, remove sorry.\n\t* c-c++-common/gomp/allocate-19.c: Remove sorry, add C++ diags.\n\t* c-c++-common/gomp/allocate-allocator-handle.h: New header.\n\t* c-c++-common/gomp/directive-1.c: Remove sorry.\n\t* c-c++-common/gomp/uses_allocators-1.c: Remove sorry.\n\t* g++.dg/gomp/allocate-5.C: New test.\n\t* g++.dg/gomp/allocate-6.C: New test.\n\t* g++.dg/gomp/allocate-7.C: New test.\n\t* g++.dg/gomp/allocate-8.C: New test.\n\t* g++.dg/gomp/allocate-9.C: New test.\n\t* g++.dg/gomp/allocate-10.C: New test.\n\t* g++.dg/gomp/allocate-11.C: New test.\n\t* g++.dg/gomp/allocate-12.C: New test.\n\t* g++.dg/gomp/allocate-13.C: New test.\n\t* g++.dg/gomp/allocate-handles-1.C: New test.\n\t* g++.dg/gomp/allocate-handles-2.C: New test.\n\t* g++.dg/gomp/allocate-allocator-handle.h: New header.\n\nSigned-off-by: Waffl3x <waffl3x@baylibre.com>\n---\n gcc/cp/constexpr.cc                           |   2 +\n gcc/cp/cp-tree.def                            |  11 +\n gcc/cp/cp-tree.h                              |  17 +\n gcc/cp/parser.cc                              | 138 +++---\n gcc/cp/pt.cc                                  |  26 ++\n gcc/cp/semantics.cc                           | 326 ++++++++++++++\n gcc/gimplify.cc                               |  10 +-\n gcc/testsuite/c-c++-common/gomp/allocate-10.c |   5 +-\n gcc/testsuite/c-c++-common/gomp/allocate-11.c |  29 +-\n gcc/testsuite/c-c++-common/gomp/allocate-12.c |  39 +-\n gcc/testsuite/c-c++-common/gomp/allocate-14.c |   3 -\n gcc/testsuite/c-c++-common/gomp/allocate-15.c |   3 -\n gcc/testsuite/c-c++-common/gomp/allocate-16.c |  21 +-\n gcc/testsuite/c-c++-common/gomp/allocate-17.c |   2 +-\n gcc/testsuite/c-c++-common/gomp/allocate-18.c |  18 +-\n gcc/testsuite/c-c++-common/gomp/allocate-19.c |  27 +-\n gcc/testsuite/c-c++-common/gomp/allocate-5.c  |  31 +-\n gcc/testsuite/c-c++-common/gomp/allocate-9.c  |  88 ++--\n .../gomp/allocate-allocator-handle.h          |  19 +\n gcc/testsuite/c-c++-common/gomp/directive-1.c |   1 -\n .../c-c++-common/gomp/uses_allocators-1.c     |   4 +-\n gcc/testsuite/g++.dg/gomp/allocate-10.C       | 115 +++++\n gcc/testsuite/g++.dg/gomp/allocate-11.C       |  18 +\n gcc/testsuite/g++.dg/gomp/allocate-12.C       |  38 ++\n gcc/testsuite/g++.dg/gomp/allocate-13.C       |  28 ++\n gcc/testsuite/g++.dg/gomp/allocate-5.C        | 411 ++++++++++++++++++\n gcc/testsuite/g++.dg/gomp/allocate-6.C        | 392 +++++++++++++++++\n gcc/testsuite/g++.dg/gomp/allocate-7.C        | 222 ++++++++++\n gcc/testsuite/g++.dg/gomp/allocate-8.C        |  48 ++\n gcc/testsuite/g++.dg/gomp/allocate-9.C        |  78 ++++\n .../g++.dg/gomp/allocate-allocator-handle.h   |  18 +\n .../g++.dg/gomp/allocate-handles-1.C          |  63 +++\n .../g++.dg/gomp/allocate-handles-2.C          |  45 ++\n libgomp/testsuite/libgomp.c++/allocate-2.C    | 329 ++++++++++++++\n .../allocate-4.c                              |   3 -\n .../allocate-5.c                              |   3 -\n .../allocate-6.c                              |   3 -\n .../allocate-7.c                              |   3 -\n 38 files changed, 2435 insertions(+), 202 deletions(-)\n create mode 100644 gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-10.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-11.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-12.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-13.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-5.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-6.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-7.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-8.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-9.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-handles-1.C\n create mode 100644 gcc/testsuite/g++.dg/gomp/allocate-handles-2.C\n create mode 100644 libgomp/testsuite/libgomp.c++/allocate-2.C\n rename libgomp/testsuite/{libgomp.c => libgomp.c-c++-common}/allocate-4.c (95%)\n rename libgomp/testsuite/{libgomp.c => libgomp.c-c++-common}/allocate-5.c (96%)\n rename libgomp/testsuite/{libgomp.c => libgomp.c-c++-common}/allocate-6.c (98%)\n rename libgomp/testsuite/{libgomp.c => libgomp.c-c++-common}/allocate-7.c (90%)","diff":"diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc\nindex e9388a6ba7f..43d9037eeb5 100644\n--- a/gcc/cp/constexpr.cc\n+++ b/gcc/cp/constexpr.cc\n@@ -10620,6 +10620,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,\n     case OMP_ATOMIC_CAPTURE_OLD:\n     case OMP_ATOMIC_CAPTURE_NEW:\n     case OMP_DEPOBJ:\n+    case OMP_ALLOCATE:\n     case OACC_PARALLEL:\n     case OACC_KERNELS:\n     case OACC_SERIAL:\n@@ -12491,6 +12492,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,\n     case OMP_ATOMIC_CAPTURE_OLD:\n     case OMP_ATOMIC_CAPTURE_NEW:\n     case OMP_DEPOBJ:\n+    case OMP_ALLOCATE:\n     case OACC_PARALLEL:\n     case OACC_KERNELS:\n     case OACC_SERIAL:\ndiff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def\nindex 3826b143a96..47f29bada24 100644\n--- a/gcc/cp/cp-tree.def\n+++ b/gcc/cp/cp-tree.def\n@@ -495,6 +495,17 @@ DEFTREECODE (TEMPLATE_INFO, \"template_info\", tcc_exceptional, 0)\n    Operand 1: OMP_DEPOBJ_CLAUSES: List of clauses.  */\n DEFTREECODE (OMP_DEPOBJ, \"omp_depobj\", tcc_statement, 2)\n \n+/* OpenMP - #pragma omp allocate\n+   Underlying node type is tree_exp, used to represent the directive as a\n+   statement in a function.  Only used for template instantiation.\n+   Operand 0: OMP_ALLOCATE_VARS: tree_list containing each var_decl passed to\n+\t\t\t\t the directive as an args, purpose contains the\n+\t\t\t\t var_decl, value contains a expr that holds the\n+\t\t\t\t location where the var was passed in.\n+   Operand 1: OMP_ALLOCATE_ALLOCATOR: Expr of the allocator clause.\n+   Operand 2: OMP_ALLOCATE_ALIGN: Expr of the align clause.  */\n+DEFTREECODE (OMP_ALLOCATE, \"omp_allocate\", tcc_statement, 3)\n+\n /* Extensions for Concepts. */\n \n /* Used to represent information associated with constrained declarations. */\ndiff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h\nindex 70132b586d8..d8433a0519e 100644\n--- a/gcc/cp/cp-tree.h\n+++ b/gcc/cp/cp-tree.h\n@@ -6005,6 +6005,21 @@ target_expr_needs_replace (tree t)\n #define OMP_DEPOBJ_DEPOBJ(NODE)\t TREE_OPERAND (OMP_DEPOBJ_CHECK (NODE), 0)\n #define OMP_DEPOBJ_CLAUSES(NODE) TREE_OPERAND (OMP_DEPOBJ_CHECK (NODE), 1)\n \n+/* OMP_ALLOCATE accessors.\n+   #pragma omp allocate(var1, var2) allocator(Expr) align(Expr) */\n+/* Contains a tree_list containing each variable passed as an argument to the\n+   allocate directive.  The purpose holds the var_decl, the value holds an expr\n+   containing the location the var was passed as an argument.  */\n+#define OMP_ALLOCATE_VARS(NODE) (TREE_OPERAND (OMP_ALLOCATE_CHECK (NODE), 0))\n+/* Contains the expression passed in the allocator clause, may be NULL_TREE if\n+   no clause was provided, or error_mark_node if errors occurred.  */\n+#define OMP_ALLOCATE_ALLOCATOR(NODE) \\\n+  (TREE_OPERAND (OMP_ALLOCATE_CHECK (NODE), 1))\n+/* Contains the expression passed in the align clause, may be NULL_TREE if no\n+   clause was provided, or error_mark_node if errors occurred.  */\n+#define OMP_ALLOCATE_ALIGN(NODE) \\\n+  (TREE_OPERAND (OMP_ALLOCATE_CHECK (NODE), 2))\n+\n /* An enumeration of the kind of tags that C++ accepts.  */\n enum tag_types {\n   none_type = 0, /* Not a tag type.  */\n@@ -8562,6 +8577,8 @@ extern tree finish_omp_for\t\t\t(location_t, enum tree_code,\n \t\t\t\t\t\t tree, tree, tree, tree, tree,\n \t\t\t\t\t\t tree, tree, vec<tree> *, tree);\n extern tree finish_omp_for_block\t\t(tree, tree);\n+extern void finish_omp_allocate\t\t\t(location_t, tree, tree, tree,\n+\t\t\t\t\t\t tree);\n extern void finish_omp_atomic\t\t\t(location_t, enum tree_code,\n \t\t\t\t\t\t enum tree_code, tree, tree,\n \t\t\t\t\t\t tree, tree, tree, tree, tree,\ndiff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc\nindex ed24621452a..2cec22a9386 100644\n--- a/gcc/cp/parser.cc\n+++ b/gcc/cp/parser.cc\n@@ -47184,11 +47184,85 @@ cp_parser_omp_structured_block (cp_parser *parser, bool *if_p)\n static void\n cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok)\n {\n-  tree allocator = NULL_TREE;\n-  tree alignment = NULL_TREE;\n-  location_t loc = pragma_tok->location;\n-  tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ALLOCATE, NULL_TREE);\n+  tree nl = cp_parser_omp_var_list (parser, OMP_CLAUSE_ERROR, NULL_TREE);\n+\n+  {\n+    /* The head might have an error and need to be removed.  */\n+    tree *chain = &nl;\n+    for (tree node = nl; node != NULL_TREE; node = TREE_CHAIN (node))\n+      {\n+\tconst tree var = TREE_PURPOSE (node);\n+\tconst tree arg_loc_wrapper = TREE_VALUE (node);\n+\tconst location_t arg_loc = EXPR_LOCATION (arg_loc_wrapper);\n+\n+\ttree attr = lookup_attribute (\"omp allocate\",\n+\t\t\t\t      DECL_ATTRIBUTES (var));\n+\tif (attr)\n+\t  {\n+\t    auto_diagnostic_group d;\n+\t    error_at (arg_loc,\n+\t\t      \"%qD already appeared as list item in an \"\n+\t\t      \"%<allocate%> directive\", var);\n+\t    const location_t old_arg_loc = [&] ()\n+\t      {\n+\t\ttree attr_value = TREE_VALUE (attr);\n+\t\tif (TREE_CODE (attr_value) == NOP_EXPR)\n+\t\t  return EXPR_LOCATION (attr_value);\n+\t\t/* If the previous directive that has this var as an arg was\n+\t\t   already finished by finish_omp_allocate, attr_value is a\n+\t\t   tree_list instead.  */\n+\t\tgcc_assert (TREE_CODE (attr_value) == TREE_LIST);\n+\t\t/* In this case the location wrapper is stored differently.  */\n+\t\treturn EXPR_LOCATION (TREE_CHAIN (attr_value));\n+\t      } (); /* IILE.  */\n+\t    inform (old_arg_loc,\n+\t\t    \"%qD previously appeared here\", var);\n+\t    /* Remove the node.  */\n+\t    *chain = TREE_CHAIN (node);\n+\t  }\n+\telse\n+\t  {\n+\t    /* Mark the variable as having appeared in an allocate directive.\n+\t       Do this even if any of the clauses are dependent, we need to\n+\t       know this variable appeared in a directive before instantiation\n+\t       to emit correct diagnostics.  Stash the arg's location here for\n+\t       better diagnostics.\n+\n+\t       There is a lot of subtle complexity here because the directive\n+\t       syntactically appears after the declarations of its arguments,\n+\t       so processing this directive only starts after the arguments\n+\t       have been processed.  This much is obvious, the issue is adding\n+\t       an attribute to each VAR_DECL after they have been processed is\n+\t       fairly intrusive, we need to take care to not break invariants\n+\t       that those processes set up.  In particular, attributes are\n+\t       ordered by dependency, dependent first, non-dependent after.\n+\t       There are parts of the compiler that expect this ordering.  If\n+\t       we naively chain the \"omp allocate\" attr to the front it will\n+\t       effectively shadow dependent attributes, and if we chain it to\n+\t       the end we will overwrite data in non-dependent attributes.\n+\t       The solution is to chain it after the last dependent attribute,\n+\t       this is still fairly brittle but that is the consequence of this\n+\t       feature.  */\n+\t    {\n+\t      tree *const attr_chain = [&] ()\n+\t\t{\n+\t\t  tree *p = &DECL_ATTRIBUTES (var);\n+\t\t  while (*p != NULL_TREE && ATTR_IS_DEPENDENT (*p))\n+\t\t    p = &TREE_CHAIN (*p);\n+\t\t  return p;\n+\t\t} (); /* IILE.  */\n+\t      *attr_chain = tree_cons (get_identifier (\"omp allocate\"),\n+\t\t\t\t       arg_loc_wrapper,\n+\t\t\t\t       *attr_chain);\n+\t    }\n+\t    /* Keep the node.  */\n+\t    chain = &TREE_CHAIN (node);\n+\t  }\n+      }\n+  }\n \n+  cp_expr allocator = NULL_TREE;\n+  cp_expr alignment = NULL_TREE;\n   do\n     {\n       if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA)\n@@ -47209,70 +47283,30 @@ cp_parser_omp_allocate (cp_parser *parser, cp_token *pragma_tok)\n \t}\n       if (!parens.require_open (parser))\n \tbreak;\n-      tree expr = cp_parser_assignment_expression (parser);\n+      cp_expr expr = cp_parser_assignment_expression (parser);\n       if (p[2] == 'i' && alignment)\n \t{\n \t  error_at (cloc, \"too many %qs clauses\", \"align\");\n \t  break;\n \t}\n       else if (p[2] == 'i')\n-\t{\n-\t  if (expr != error_mark_node)\n-\t    alignment = expr;\n-\t  /* FIXME: Remove when adding check to semantics.cc; cf FIXME below. */\n-\t  if (alignment\n-\t      && !type_dependent_expression_p (alignment)\n-\t      && !INTEGRAL_TYPE_P (TREE_TYPE (alignment)))\n-\t    {\n-\t      error_at (cloc, \"%<align%> clause argument needs to be \"\n-\t\t\t      \"positive constant power of two integer \"\n-\t\t\t      \"expression\");\n-\t      alignment = NULL_TREE;\n-\t    }\n-\t  else if (alignment)\n-\t    {\n-\t      alignment = mark_rvalue_use (alignment);\n-\t      if (!processing_template_decl)\n-\t\t{\n-\t\t  alignment = maybe_constant_value (alignment);\n-\t\t  if (TREE_CODE (alignment) != INTEGER_CST\n-\t\t      || !tree_fits_uhwi_p (alignment)\n-\t\t      || !integer_pow2p (alignment))\n-\t\t    {\n-\t\t      error_at (cloc, \"%<align%> clause argument needs to be \"\n-\t\t\t\t      \"positive constant power of two integer \"\n-\t\t\t\t      \"expression\");\n-\t\t      alignment = NULL_TREE;\n-\t\t    }\n-\t\t}\n-\t    }\n-\t}\n+\talignment = expr;\n       else if (allocator)\n \t{\n \t  error_at (cloc, \"too many %qs clauses\", \"allocator\");\n \t  break;\n \t}\n       else\n-\t{\n-\t  if (expr != error_mark_node)\n-\t    allocator = expr;\n-\t}\n+\tallocator = expr;\n       parens.require_close (parser);\n     } while (true);\n   cp_parser_require_pragma_eol (parser, pragma_tok);\n \n-  if (allocator || alignment)\n-    for (tree c = nl; c != NULL_TREE; c = OMP_CLAUSE_CHAIN (c))\n-      {\n-\tOMP_CLAUSE_ALLOCATE_ALLOCATOR (c) = allocator;\n-\tOMP_CLAUSE_ALLOCATE_ALIGN (c) = alignment;\n-      }\n-\n-  /* FIXME: When implementing properly, delete the align/allocate expr error\n-     check above and add one in semantics.cc (to properly handle templates).\n-     Base this on the allocator/align modifiers check for the 'allocate' clause\n-     in semantics.cc's finish_omp_clauses.  */\n-  sorry_at (loc, \"%<#pragma omp allocate%> not yet supported\");\n+  finish_omp_allocate (pragma_tok->location,\n+\t\t       nl,\n+\t\t       allocator,\n+\t\t       alignment,\n+\t\t       current_scope ());\n }\n \n /* OpenMP 2.5:\ndiff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc\nindex 05ecda2a0d8..aa41a46e007 100644\n--- a/gcc/cp/pt.cc\n+++ b/gcc/cp/pt.cc\n@@ -20466,6 +20466,32 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)\n \t\t\t   OMP_DEPOBJ_CLAUSES (t));\n       break;\n \n+    case OMP_ALLOCATE:\n+      {\n+\tgcc_assert (flag_openmp);\n+\n+\ttree alloc\n+\t  = tsubst_expr (OMP_ALLOCATE_ALLOCATOR (t), args, complain, in_decl);\n+\ttree align\n+\t  = tsubst_expr (OMP_ALLOCATE_ALIGN (t), args, complain, in_decl);\n+\ttree vars = copy_list (OMP_ALLOCATE_VARS (t));\n+\tfor (tree node = vars; node != NULL_TREE; node = TREE_CHAIN (node))\n+\t  {\n+\t    if (TREE_PURPOSE (node) == error_mark_node)\n+\t      continue;\n+\t    /* The var was already substituted, just look up the new node.  */\n+\t    tree var = lookup_name (DECL_NAME (TREE_PURPOSE (node)),\n+\t\t\t\t    LOOK_where::BLOCK, LOOK_want::NORMAL);\n+\t    /* There's a weird edge case where lookup_name returns NULL_TREE,\n+\t       but only for incorrect code.  Even so, handle NULL_TREE to avoid\n+\t       segfaulting in those cases.  */\n+\t    TREE_PURPOSE (node) = var ? var : error_mark_node;\n+\t    /* Don't copy the attr here, let finish_omp_allocate handle it.  */\n+\t  }\n+\tfinish_omp_allocate (EXPR_LOCATION (t), vars, alloc, align,\n+\t\t\t     current_scope ());\n+\tbreak;\n+      }\n     case OACC_DATA:\n     case OMP_TARGET_DATA:\n     case OMP_TARGET:\ndiff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc\nindex 92ec4836a6f..ac81bbe9317 100644\n--- a/gcc/cp/semantics.cc\n+++ b/gcc/cp/semantics.cc\n@@ -12354,6 +12354,332 @@ finish_omp_for_block (tree bind, tree omp_for)\n   return bind;\n }\n \n+/* Validate an OpenMP allocate directive, then add the ALLOC and ALIGN exprs to\n+   the \"omp allocate\" attr of each decl found in VARS.  The value of attr is\n+   a TREE_LIST with ALLOC stored in its purpose member and ALIGN stored in its\n+   value member.  ALLOC and ALIGN are exprs passed as arguments to the\n+   allocator and align clauses of the directive.  VARS may be NULL_TREE if\n+   there were errors during parsing.\n+   #pragma omp allocate(VARS) allocator(ALLOC) align(ALIGN)\n+\n+   If processing_template_decl, a stmt of tree_code OMP_ALLOCATE is added to\n+   the function instead.  LOC is used to initialize EXPR_LOCATION of the stmt.\n+\n+   If any errors occur, the \"omp allocate\" attr is marked so the middle end\n+   knows to skip it during gimplification.  */\n+\n+void\n+finish_omp_allocate (const location_t loc, const tree var_list,\n+\t\t     const tree alloc_in, const tree align_in,\n+\t\t     const tree context)\n+{\n+  /* Takes a VAR_DECL.  */\n+  using var_predicate = bool (*)(const_tree);\n+  const auto any_of_vars = [&var_list] (var_predicate predicate)\n+    {\n+      for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn))\n+\tif (predicate (TREE_PURPOSE (vn)))\n+\t  return true;\n+      return false;\n+    };\n+  const bool global = !DECL_DECLARES_FUNCTION_P (context);\n+  const var_predicate tree_static_p = [&] () -> var_predicate\n+    {\n+      /* If this directive is not in a function, all the vars are static.  */\n+      if (global)\n+\treturn [] (const_tree) -> bool { return true; };\n+      else\n+\treturn [] (const_tree t) -> bool { return TREE_STATIC (t); };\n+    } (); /* IILE.  */\n+  const bool any_static_vars = any_of_vars (tree_static_p);\n+\n+  /* (OpenMP 5.2, 174:15) Type: expression of integer type\n+\t\t\t  Properties: constant, positive  */\n+  const tree align = [&align_in] ()\n+    {\n+      if (!align_in || align_in == error_mark_node)\n+\treturn align_in;\n+\n+      auto emit_align_err = [&align_in] ()\n+\t{\n+\t  error_at (EXPR_LOCATION (align_in),\n+\t\t    \"%<align%> clause argument needs to be positive constant \"\n+\t\t    \"power of two integer expression\");\n+\t};\n+      if (processing_template_decl)\n+\treturn align_in;\n+\n+      /* Identity conversions are okay.  */\n+      const tree converted_align\n+\t= build_converted_constant_expr (size_type_node, align_in, tf_error);\n+      gcc_assert (converted_align != NULL_TREE);\n+      if (converted_align == error_mark_node)\n+\t{\n+\t  emit_align_err ();\n+\t  return error_mark_node;\n+\t}\n+\n+      const tree folded_align = cxx_constant_value (converted_align);\n+      if (folded_align == error_mark_node)\n+\t{\n+\t  emit_align_err ();\n+\t  return error_mark_node;\n+\t}\n+      gcc_assert (TREE_CONSTANT (folded_align));\n+      /* (OpenMP 5.2, 175:10) alignment must evaluate to a power of two.  */\n+      if (tree_int_cst_sgn (folded_align) != 1\n+\t  || !integer_pow2p (folded_align))\n+\t{\n+\t  emit_align_err ();\n+\t  return error_mark_node;\n+\t}\n+      return folded_align;\n+    } (); /* IILE.  */\n+\n+  /* (OpenMP 5.2, 175:17) Type: expression of allocator_handle type\n+\t\t\t  Properties: default  */\n+  const tree alloc = [&] ()\n+    {\n+      if (alloc_in == error_mark_node)\n+\treturn error_mark_node;\n+      /* (OpenMP 5.2 176:26-28)\n+\t If a list item has static storage duration, the allocator clause must\n+\t be specified and the allocator expression in the clause must be a\n+\t constant expression that evaluates to one of the predefined memory\n+\t allocator values.  */\n+      const auto emit_diag_for_static_vars = [&] ()\n+\t{\n+\t  inform (UNKNOWN_LOCATION,\n+\t\t  \"because one or more variables with static storage duration \"\n+\t\t  \"appear in the %<allocate%> directive\");\n+\t};\n+      if (alloc_in == NULL_TREE)\n+\t{\n+\t  if (!any_static_vars)\n+\t    return NULL_TREE;\n+\t  else\n+\t    {\n+\t      auto_diagnostic_group d;\n+\t      error_at (loc, \"%<allocator%> clause must be specified\");\n+\t      emit_diag_for_static_vars ();\n+\t      return error_mark_node;\n+\t    }\n+\t  gcc_unreachable ();\n+\t}\n+      if (processing_template_decl)\n+\treturn alloc_in;\n+\n+      const tree alloc_type = [&] ()\n+\t{\n+\t  static tree cached_alloc_type = NULL_TREE;\n+\t  if (cached_alloc_type != NULL_TREE)\n+\t    return cached_alloc_type;\n+\n+\t  const tree alloc_type_decl\n+\t    = lookup_qualified_name (global_namespace,\n+\t\t\t\t     get_identifier (\"omp_allocator_handle_t\"),\n+\t\t\t\t     LOOK_want::TYPE);\n+\t  /* User didn't include omp.h...  */\n+\t  if (alloc_type_decl == error_mark_node)\n+\t    {\n+\t      /* We need to wait until instantiation to complain about it if\n+\t\t alloc_in could possibly be well-formed.  */\n+\t      if (processing_template_decl\n+\t\t  && (type_dependent_expression_p (alloc_in)\n+\t\t\t /* No one sane would have a conversion template and\n+\t\t\t    use it this way, but it would still technically be\n+\t\t\t    correct so we have to account for it.  */\n+\t\t      || (CLASS_TYPE_P (TREE_TYPE (alloc_in))\n+\t\t\t  && TYPE_HAS_CONVERSION (TREE_TYPE (alloc_in)))))\n+\t\t{\n+\t\t  gcc_assert (cached_alloc_type == NULL_TREE);\n+\t\t  return cached_alloc_type;\n+\t\t}\n+\t      auto_diagnostic_group d;\n+\t      error_at (EXPR_LOCATION (alloc_in),\n+\t\t\t\"%<allocator%> clause requires a valid declaration \"\n+\t\t\t\"of %<omp_allocator_handle_t%>\");\n+\t      inform (EXPR_LOCATION (alloc_in),\n+\t\t      \"%<omp_allocator_handle_t%> is defined in header \"\n+\t\t      \"%<<omp.h>%>; this is probably fixable by adding \"\n+\t\t      \"%<#include <omp.h>%>\");\n+\t      cached_alloc_type = error_mark_node;\n+\t      return cached_alloc_type;\n+\t    }\n+\t  const auto underlying_type_is_valid = [] (const const_tree type)\n+\t    {\n+\t      const const_tree canonical_type = TYPE_CANONICAL (type);\n+\t      if (cxx_dialect >= cxx11)\n+\t\treturn canonical_type == uintptr_type_node;\n+\t      gcc_assert (cxx_dialect < cxx11);\n+\t      /* Pre c++11 we can't just assume the underlying type.  */\n+\t      return TYPE_UNSIGNED (canonical_type) == true\n+\t\t     && wi::to_widest (TYPE_SIZE (canonical_type))\n+\t\t\t== wi::to_widest (TYPE_SIZE (uintptr_type_node))\n+\t\t     && wi::to_widest (TYPE_MIN_VALUE (canonical_type))\n+\t\t\t== wi::to_widest (TYPE_MIN_VALUE (uintptr_type_node))\n+\t\t     && wi::to_widest (TYPE_MAX_VALUE (canonical_type))\n+\t\t\t== wi::to_widest (TYPE_MAX_VALUE (uintptr_type_node));\n+\t    };\n+\t  const tree alloc_type = TREE_TYPE (alloc_type_decl);\n+\t  if (TREE_CODE (alloc_type) != ENUMERAL_TYPE\n+\t      || !underlying_type_is_valid (TREE_TYPE (alloc_type)))\n+\t    {\n+\t      auto_diagnostic_group d;\n+\t      error_at (EXPR_LOCATION (alloc_in),\n+\t\t\t\"%<allocator%> clause requires a valid declaration \"\n+\t\t\t\"of %<omp_allocator_handle_t%>\");\n+\t      /* This diagnostic is not very good, especially if it somehow\n+\t\t is the declaration in omp.h.  */\n+\t      inform (DECL_SOURCE_LOCATION (alloc_type_decl),\n+\t\t      \"invalid type declared here\");\n+\t      cached_alloc_type = error_mark_node;\n+\t    }\n+\t  else\n+\t    cached_alloc_type = alloc_type;\n+\t  gcc_assert (cached_alloc_type != NULL_TREE);\n+\t  return cached_alloc_type;\n+\t} (); /* IILE.  */\n+      /* Any sane user will include omp.h before we get here, unfortunately\n+\t insane doesn't mean incorrect.  */\n+      if (alloc_type == NULL_TREE)\n+\treturn alloc_in;\n+      /* We can't do anything meaningful with alloc_in if we lack a valid\n+\t omp_allocator_handle_t type.  */\n+      if (alloc_type == error_mark_node)\n+\treturn error_mark_node;\n+\n+      /* Identity conversion is okay.  */\n+      const tree converted_alloc\n+\t= any_static_vars\n+\t  ? build_converted_constant_expr (alloc_type, alloc_in, tf_error)\n+\t  : perform_implicit_conversion (alloc_type, alloc_in, tf_error);\n+\n+      /* The expr is not manifestly constant-evaluated in this case, only do\n+\t simple folding even if it is potentially a constant expression.  */\n+      if (!any_static_vars)\n+\treturn maybe_fold_non_dependent_expr (converted_alloc, tf_none);\n+\n+      /* Per OpenMP 5.2 176:26-28 quoted above, the allocator clause is\n+\t manifestly constant-evaluated in this case.  */\n+      const tree ret = cxx_constant_value (converted_alloc);\n+      if (ret == error_mark_node)\n+\t{\n+\t  auto_diagnostic_group d;\n+\t  error_at (EXPR_LOCATION (alloc_in),\n+\t\t    \"%<allocator%> clause requires a constant \"\n+\t\t    \"predefined allocator\");\n+\t  emit_diag_for_static_vars ();\n+\t  return error_mark_node;\n+\t}\n+      gcc_assert (TREE_CONSTANT (ret));\n+      wi::tree_to_widest_ref alloc_value = wi::to_widest (ret);\n+      /* MAX is inclusive.  */\n+      const bool predefined_allocator_p\n+\t= (alloc_value >= 1\n+\t   && alloc_value <= GOMP_OMP_PREDEF_ALLOC_MAX)\n+\t  || (alloc_value >= GOMP_OMPX_PREDEF_ALLOC_MIN\n+\t      && alloc_value <= GOMP_OMPX_PREDEF_ALLOC_MAX);\n+      if (!predefined_allocator_p)\n+\t{\n+\t  auto_diagnostic_group d;\n+\t  error_at (EXPR_LOCATION (alloc_in),\n+\t\t    \"%<allocator%> clause requires a constant \"\n+\t\t    \"predefined allocator\");\n+\t  emit_diag_for_static_vars ();\n+\t  inform (EXPR_LOCATION (alloc_in),\n+\t\t  \"expression evaluates to %qwu\", tree_to_uhwi (ret));\n+\t  return error_mark_node;\n+\t}\n+      return ret;\n+    } (); /* IILE.  */\n+\n+  auto finalize_allocate_attr = [] (tree var, tree alloc, tree align)\n+    {\n+      /* This should only be called once for each var, either after a\n+\t diagnostic, or when we are finished with the directive.  */\n+      gcc_assert (var != NULL_TREE && var != error_mark_node);\n+\n+      /* Replace the old attr in place to maintain the invariants of\n+\t DECL_ATTRIBUTES as described in cp_parser_omp_allocate.\n+\t Unfortunately lookup_attribute does not return an \"iterator\" to the\n+\t attr so we have to write the lookup ourselves here.  */\n+      tree *const attr_it = [&] ()\n+\t{\n+\t  tree *it = &DECL_ATTRIBUTES (var);\n+\t  for (; *it != NULL_TREE; it = &TREE_CHAIN (*it))\n+\t    if (is_attribute_p (\"omp allocate\", get_attribute_name (*it)))\n+\t      return it;\n+\t  gcc_unreachable ();\n+\t} (); /* IILE.  */\n+      /* Don't modify the old attr, for various reasons it isn't marked\n+\t dependent so substitution does not copy it.  */\n+      const_tree old_attr = *attr_it;\n+      /* There should only be one attr.  */\n+      gcc_checking_assert (!lookup_attribute (\"omp allocate\",\n+\t\t\t\t\t      TREE_CHAIN (old_attr)));\n+      /* This location is where the var was specified in the directive.  */\n+      const tree arg_loc = TREE_VALUE (old_attr);\n+      gcc_assert (arg_loc != NULL_TREE && TREE_CODE (arg_loc) == NOP_EXPR);\n+      /* Smuggle the location, we might still need it for diagnostics, such as\n+\t if we are still parsing a function.  */\n+      const tree attr_value = tree_cons (alloc, align, arg_loc);\n+\n+      /* As noted above, we create the final attr ourselves instead of having\n+\t substitution handle it.  */\n+      *attr_it = tree_cons (TREE_PURPOSE (old_attr),\n+\t\t\t    attr_value,\n+\t\t\t    TREE_CHAIN (old_attr));\n+    };\n+  auto finalize_var_node_with_error = [&] (tree node)\n+    {\n+      /* We can't remove the attribute, the variable still needs to be marked\n+\t in case that we are still parsing a function for the first time.\n+\t We avoid handling error_mark_node in varpool_node::finalize_decl by\n+\t setting align to NULL_TREE.  */\n+      finalize_allocate_attr (TREE_PURPOSE (node), error_mark_node, NULL_TREE);\n+      /* Prevent further diagnostics for this var.  */\n+      TREE_PURPOSE (node) = error_mark_node;\n+    };\n+\n+  /* Even if there have been errors, save the current state, there might be\n+     more to diagnose on a later instantiation.  */\n+  if (processing_template_decl)\n+    {\n+      tree allocate_stmt = make_node (OMP_ALLOCATE);\n+      /* This shouldn't matter, but better to set it anyway.  */\n+      TREE_SIDE_EFFECTS (allocate_stmt) = 0;\n+      SET_EXPR_LOCATION (allocate_stmt, loc);\n+      OMP_ALLOCATE_VARS (allocate_stmt) = var_list;\n+      OMP_ALLOCATE_ALLOCATOR (allocate_stmt) = alloc;\n+      OMP_ALLOCATE_ALIGN (allocate_stmt) = align;\n+      add_stmt (allocate_stmt);\n+      return;\n+    }\n+  else if (alloc == error_mark_node || align == error_mark_node || !var_list\n+\t   || any_of_vars (&error_operand_p))\n+    {\n+      /* The directive is fully instantiated, but there were errors.  */\n+      for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn))\n+\t/* Don't mark vars a second time.  */\n+\tif (TREE_PURPOSE (vn) != error_mark_node)\n+\t  finalize_var_node_with_error (vn);\n+      return;\n+    }\n+\n+  gcc_assert (!processing_template_decl\n+\t      && alloc != error_mark_node\n+\t      && align != error_mark_node\n+\t      && var_list != NULL_TREE\n+\t      && !any_of_vars (&error_operand_p));\n+\n+  /* We can technically finalize earlier if everything (vars, alloc, align) is\n+     substituted and the alloc expr doesn't contain any local variables, this\n+     isn't worth the added complexity for now though.  */\n+  for (tree vn = var_list; vn != NULL_TREE; vn = TREE_CHAIN (vn))\n+    finalize_allocate_attr (TREE_PURPOSE (vn), alloc, align);\n+}\n+\n void\n finish_omp_atomic (location_t loc, enum tree_code code, enum tree_code opcode,\n \t\t   tree lhs, tree rhs, tree v, tree lhs1, tree rhs1, tree r,\ndiff --git a/gcc/gimplify.cc b/gcc/gimplify.cc\nindex 8bfc71315f9..4fa834ed38d 100644\n--- a/gcc/gimplify.cc\n+++ b/gcc/gimplify.cc\n@@ -1440,11 +1440,19 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)\n \t      && DECL_CONTEXT (t) == current_function_decl\n \t      && TREE_USED (t)\n \t      && (attr = lookup_attribute (\"omp allocate\", DECL_ATTRIBUTES (t)))\n-\t\t != NULL_TREE)\n+\t\t != NULL_TREE\n+\t      /* In C++, bad attributes are marked instead of removed.  */\n+\t      && TREE_PURPOSE (TREE_VALUE (attr)) != error_mark_node)\n \t    {\n \t      gcc_assert (!DECL_HAS_VALUE_EXPR_P (t));\n \t      tree alloc = TREE_PURPOSE (TREE_VALUE (attr));\n \t      tree align = TREE_VALUE (TREE_VALUE (attr));\n+\t      /* The C++ front end smuggles a location through the chain field,\n+\t\t this conflicts with Fortran handling, clear it.  */\n+\t      if (TREE_CHAIN (TREE_VALUE (attr)) != NULL_TREE\n+\t\t  && TREE_CODE (TREE_CHAIN (TREE_VALUE (attr))) == NOP_EXPR)\n+\t\tTREE_CHAIN (TREE_VALUE (attr)) = NULL_TREE;\n+\n \t      /* Allocate directives that appear in a target region must specify\n \t\t an allocator clause unless a requires directive with the\n \t\t dynamic_allocators clause is present in the same compilation\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-10.c b/gcc/testsuite/c-c++-common/gomp/allocate-10.c\nindex 7e8f579871c..ed7441b6a04 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-10.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-10.c\n@@ -1,5 +1,3 @@\n-/* TODO: enable for C++ once implemented. */\n-/* { dg-do compile { target c } } */\n /* { dg-additional-options \"-Wall -fdump-tree-gimple\" } */\n \n typedef enum omp_allocator_handle_t\n@@ -23,6 +21,7 @@ void\n h1()\n {\n   omp_allocator_handle_t my_handle;\n+  /* { dg-bogus \"variable 'my_handle' set but not used\" \"\" { target *-*-* } .-1 } */\n   int B1[3]; /* { dg-warning \"'my_handle' is used uninitialized\" } */\n   /* { dg-warning \"variable 'B1' set but not used\" \"\" { target *-*-* } .-1 } */\n   #pragma omp allocate(B1) allocator(my_handle)\n@@ -36,6 +35,7 @@ void\n h2()\n {\n   omp_allocator_handle_t my_handle;\n+  /* { dg-bogus \"variable 'my_handle' set but not used\" \"\" { target *-*-* } .-1 } */\n   int B2[3];  /* { dg-warning \"unused variable 'B2'\" } */\n   #pragma omp allocate(B2) allocator(my_handle) /* No warning as 'B2' is unused */\n }\n@@ -44,6 +44,7 @@ void\n h3()\n {\n   omp_allocator_handle_t my_handle;\n+  /* { dg-bogus \"variable 'my_handle' set but not used\" \"\" { target *-*-* } .-1 } */\n   int B3[3] = {1,2,3};  /* { dg-warning \"unused variable 'B3'\" } */\n   #pragma omp allocate(B3) allocator(my_handle) /* No warning as 'B3' is unused */\n }\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-11.c b/gcc/testsuite/c-c++-common/gomp/allocate-11.c\nindex dceb97f8c5f..dd1bcd9bec8 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-11.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-11.c\n@@ -1,5 +1,9 @@\n-/* TODO: enable for C++ once implemented. */\n-/* { dg-do compile { target c } } */\n+/* This warning is effectively bogus, it appears to only trigger after UB(?)\n+   triggers an optimization that removes the case labels.  We don't actually\n+   want to check for this behavior.  If anything we might want to ensure it\n+   doesn't trigger when jumping over a variable found in an allocate directive,\n+   as we are supposed to already diagnose an error for that case.  */\n+/* { dg-additional-options -Wno-switch-unreachable } */\n \n void bar();\n void use (int*);\n@@ -7,15 +11,14 @@ void use (int*);\n void\n f (int i)\n {\n-  switch (i)  /* { dg-note \"switch starts here\" } */\n+  switch (i)  /* { dg-note \"switch starts here\" \"\" { xfail c++ } } */\n     {\n-      int j;  /* { dg-note \"'j' declared here\" } */\n+      int j;  /* { dg-note \"'j' declared here\" \"\" { xfail c++ } } */\n       #pragma omp allocate(j)\n-    case 42:  /* { dg-error \"switch jumps over OpenMP 'allocate' allocation\" } */\n+    case 42:  /* { dg-error \"switch jumps over OpenMP 'allocate' allocation\" \"\" { xfail c++ } } */\n       bar ();\n-      /* { dg-warning \"statement will never be executed \\\\\\[-Wswitch-unreachable\\\\\\]\" \"\" { target *-*-* } .-1 } */\n       break;\n-    case 51:  /* { dg-error \"switch jumps over OpenMP 'allocate' allocation\" } */\n+    case 51:  /* { dg-error \"switch jumps over OpenMP 'allocate' allocation\" \"\" { xfail c++ } } */\n       use (&j);\n       break;\n     }\n@@ -25,13 +28,17 @@ int\n h (int i2)\n {\n   if (i2 == 5)\n-    goto label; /* { dg-error \"jump skips OpenMP 'allocate' allocation\" } */\n+    goto label; /* { dg-error \"jump skips OpenMP 'allocate' allocation\" \"\" { xfail c++ } } */\n+    /* { dg-note \"from here\" \"\" { target c++ } .-1 } */\n   return 5;\n \n-  int k2;  /* { dg-note \"'k2' declared here\" } */\n-  int j2 = 4;  /* { dg-note \"'j2' declared here\" } */\n+  int k2;  /* { dg-note \"'k2' declared here\" \"\" { xfail c++ } } */\n+  int j2 = 4;  /* { dg-note \"'j2' declared here\" \"\" { xfail c++ } } */\n+  /* { dg-note \"crosses initialization of 'int j2'\" \"\" { target c++ } .-1 } */\n   #pragma omp allocate(k2, j2)\n-label:  /* { dg-note \"label 'label' defined here\" } */\n+label:  /* { dg-note \"label 'label' defined here\" \"\" { xfail c++ } } */\n+// It might make sense to make this bogus, as semantically it's assigning to the pointed at value.\n+/* { dg-error \"jump to label 'label'\" \"\" { target c++ } .-2 } */\n   k2 = 4;\n   return j2 + k2;\n }\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-12.c b/gcc/testsuite/c-c++-common/gomp/allocate-12.c\nindex 1b77db9bd6f..6a53770697c 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-12.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-12.c\n@@ -1,6 +1,3 @@\n-/* TODO: enable for C++ once implemented. */\n-/* { dg-do compile { target c } } */\n-\n typedef enum omp_allocator_handle_t\n #if __cplusplus >= 201103L\n : __UINTPTR_TYPE__\n@@ -15,9 +12,33 @@ int\n f ()\n {\n   omp_allocator_handle_t my_allocator;\n-  int n = 5;  /* { dg-note \"to be allocated variable declared here\" } */\n-  my_allocator = omp_default_mem_alloc; /* { dg-note \"modified here\" } */\n-  #pragma omp allocate(n) allocator(my_allocator)  /* { dg-error \"variable 'my_allocator' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive\" } */\n+  int n = 5;  /* { dg-note \"to be allocated variable declared here\" \"\" { xfail c++ } } */\n+  my_allocator = omp_default_mem_alloc; /* { dg-note \"modified here\" \"\" { xfail c++ } } */\n+  #pragma omp allocate(n) allocator(my_allocator)  /* { dg-error \"variable 'my_allocator' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive\" \"\" { xfail c++ } } */\n+  n = 7;\n+  return n;\n+}\n+\n+int\n+f1 ()\n+{\n+  omp_allocator_handle_t alloc;\n+  {\n+    int n = 42; /* { dg-note \"to be allocated variable declared here\" \"\" { xfail *-*-* } } */\n+    alloc = omp_default_mem_alloc; /* { dg-note \"modified here\" \"\" { xfail *-*-* } } */\n+    #pragma omp allocate(n) allocator(alloc) /* { dg-error \"variable 'alloc' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive\" \"\" { xfail *-*-* } } */\n+    n = 7;\n+    return n;\n+  }\n+}\n+\n+int\n+f2 ()\n+{\n+  omp_allocator_handle_t my_allocator;\n+  int n = 5;  /* { dg-note \"to be allocated variable declared here\" \"\" { xfail *-*-* } } */\n+  int hide_mutation = my_allocator = omp_default_mem_alloc; /* { dg-note \"modified here\" \"\" { xfail *-*-* } } */\n+  #pragma omp allocate(n) allocator(my_allocator)  /* { dg-error \"variable 'my_allocator' used in the 'allocator' clause must not be modified between declaration of 'n' and its 'allocate' directive\" \"\" { xfail *-*-* } } */\n   n = 7;\n   return n;\n }\n@@ -26,9 +47,9 @@ f ()\n int\n g ()\n {\n-  int n = 5;  /* { dg-note \"to be allocated variable declared here\" } */\n-  omp_allocator_handle_t my_allocator = omp_low_lat_mem_alloc;  /* { dg-note \"declared here\" } */\n-  #pragma omp allocate(n) allocator(my_allocator)  /* { dg-error \"variable 'my_allocator' used in the 'allocator' clause must be declared before 'n'\" } */\n+  int n = 5;  /* { dg-note \"to be allocated variable declared here\" \"\" { xfail c++ } } */\n+  omp_allocator_handle_t my_allocator = omp_low_lat_mem_alloc;  /* { dg-note \"declared here\" \"\" { xfail c++ } } */\n+  #pragma omp allocate(n) allocator(my_allocator)  /* { dg-error \"variable 'my_allocator' used in the 'allocator' clause must be declared before 'n'\" \"\" { xfail c++ } } */\n   n = 7;\n   return n;\n }\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-14.c b/gcc/testsuite/c-c++-common/gomp/allocate-14.c\nindex 894921a76d5..55f3739acfa 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-14.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-14.c\n@@ -1,6 +1,3 @@\n-/* TODO: enable for C++ once implemented. */\n-/* { dg-do compile { target c } } */\n-\n #pragma omp begin declare target\n void\n f ()\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-15.c b/gcc/testsuite/c-c++-common/gomp/allocate-15.c\nindex 52cb7686b7b..b4487143504 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-15.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-15.c\n@@ -1,6 +1,3 @@\n-/* TODO: enable for C++ once implemented. */\n-/* { dg-do compile { target c } } */\n-\n #pragma omp requires dynamic_allocators\n \n #pragma omp begin declare target\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-16.c b/gcc/testsuite/c-c++-common/gomp/allocate-16.c\nindex 08738030e6a..7df9a92fd9f 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-16.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-16.c\n@@ -1,6 +1,3 @@\n-/* TODO: enable for C++ once implemented. */\n-/* { dg-do compile { target c } } */\n-\n typedef enum omp_allocator_handle_t \n #if __cplusplus >= 201103L \n : __UINTPTR_TYPE__ \n@@ -17,12 +14,15 @@ omp_allocator_handle_t foo(int, int *);\n void\n f ()\n {\n-  int v;  /* { dg-note \"to be allocated variable declared here\" } */\n-  int n = 5;\n-  int a = 1;  /* { dg-note \"declared here\" } */\n+  int v;  /* { dg-note \"to be allocated variable declared here\" \"\" { xfail c++ } } */\n+  static const int n = 5;\n+  int a = 1;\n+  /* { dg-note \"declared here\" \"\" { xfail c++ } .-1 } */\n   int b[n];\n+  /* { dg-note \"declared here\" \"\" { target c++ xfail c++ } .-1 } */\n   b[a] = 5;\n-  #pragma omp allocate (v) allocator (foo (a, &b[a]))  /* { dg-error \"variable 'a' used in the 'allocator' clause must be declared before 'v'\" } */\n+  #pragma omp allocate (v) allocator (foo (a, &b[a]))\n+  /* { dg-error \"variable 'a' used in the 'allocator' clause must be declared before 'v'\" \"\" { xfail c++ } .-1 } */\n }\n \n void\n@@ -32,7 +32,8 @@ g ()\n   int a = 1;\n   int b[n];\n   b[a] = 5;\n-  int v;  /* { dg-note \"to be allocated variable declared here\" } */\n-  a = 2;  /* { dg-note \"modified here\" } */\n-  #pragma omp allocate (v) allocator (foo (a, &b[a]))  /* { dg-error \"variable 'a' used in the 'allocator' clause must not be modified between declaration of 'v' and its 'allocate' directive\" } */\n+  int v;  /* { dg-note \"to be allocated variable declared here\" \"\" { xfail c++ } } */\n+  a = 2;  /* { dg-note \"modified here\" \"\" { xfail c++ } } */\n+  #pragma omp allocate (v) allocator (foo (a, &b[a]))\n+  /* { dg-error \"variable 'a' used in the 'allocator' clause must not be modified between declaration of 'v' and its 'allocate' directive\" \"\" { xfail c++ } .-1 } */\n }\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-17.c b/gcc/testsuite/c-c++-common/gomp/allocate-17.c\nindex f75af0c2d93..2a896cc334d 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-17.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-17.c\n@@ -20,7 +20,7 @@ one ()\n   #pragma omp target map(tofrom: result) firstprivate(n)\n     {\n       int var = 5; //, var2[n];\n-      #pragma omp allocate(var) align(128) allocator(omp_low_lat_mem_alloc) /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } } */\n+      #pragma omp allocate(var) align(128) allocator(omp_low_lat_mem_alloc)\n        var = 7;\n }\n \ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-18.c b/gcc/testsuite/c-c++-common/gomp/allocate-18.c\nindex cad79812032..620bc4ecc97 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-18.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-18.c\n@@ -1,26 +1,10 @@\n // { dg-additional-options \"-Wno-deprecated-openmp\" }\n-typedef enum omp_allocator_handle_t\n-#if __cplusplus >= 201103L\n-: __UINTPTR_TYPE__\n-#endif\n-{\n-  omp_null_allocator = 0,\n-  omp_default_mem_alloc = 1,\n-  omp_large_cap_mem_alloc = 2,\n-  omp_const_mem_alloc = 3,\n-  omp_high_bw_mem_alloc = 4,\n-  omp_low_lat_mem_alloc = 5,\n-  omp_cgroup_mem_alloc = 6,\n-  omp_pteam_mem_alloc = 7,\n-  omp_thread_mem_alloc = 8,\n-  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__\n-} omp_allocator_handle_t;\n+#include \"allocate-allocator-handle.h\"\n \n void test0 ()\n {\n   int A1[5], B1[5];\n   #pragma omp allocate(A1) align(128) allocator(omp_default_mem_alloc)\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n   #ifndef __cplusplus\n     _Static_assert (_Alignof(A1) == _Alignof(B1), \"wrong alignment\");\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-19.c b/gcc/testsuite/c-c++-common/gomp/allocate-19.c\nindex 5c5fc008b2f..cabd3875d1e 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-19.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-19.c\n@@ -1,3 +1,4 @@\n+/* Does not include allocate-allocator-handle.h due to extra values being added.  */\n typedef enum omp_allocator_handle_t\n #if __cplusplus >= 201103L\n : __UINTPTR_TYPE__\n@@ -21,7 +22,6 @@ typedef enum omp_allocator_handle_t\n \n static int A1[5] = {1,2,3,4,5}, B1[5];\n #pragma omp allocate(A1) align(128) allocator(omp_default_mem_alloc)\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n #ifndef __cplusplus\n _Static_assert (_Alignof(A1) == _Alignof(B1), \"wrong alignment\");\n@@ -32,7 +32,6 @@ static_assert (alignof(A1) == alignof(B1), \"wrong alignment\");\n \n static int *ptr;\n #pragma omp allocate(ptr) align(2) allocator(omp_default_mem_alloc)\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n #ifndef __cplusplus\n _Static_assert (_Alignof(ptr) == _Alignof(int*), \"wrong alignment\");\n@@ -46,7 +45,6 @@ get ()\n {\n   static int q = 0;\n   #pragma omp allocate(q) align(1024) allocator(omp_default_mem_alloc)\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n #ifndef __cplusplus\n   _Static_assert (_Alignof(q) == _Alignof(int), \"wrong alignment\");\n@@ -58,12 +56,19 @@ get ()\n   return &A1[q];\n }\n \n-static int invalid1, okay1, invalid2, invalid3;\n-#pragma omp allocate(invalid1) align(128) allocator(ompx_gnu_pinned_bogus_1) /* { dg-error \"'allocator' clause requires a predefined allocator as 'invalid1' is static\" \"\" { xfail c++ } }  */\n+static int invalid1, okay1, invalid2, invalid3; /* { dg-note \"'invalid\\[123\\]' declared here\" \"\" { target c++ xfail c++ } } */\n+#pragma omp allocate(invalid1) align(128) allocator(ompx_gnu_pinned_bogus_1) /* { dg-error \"'allocator' clause requires a predefined allocator as 'invalid1' is static\" \"\" { target c } }  */\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target c++ } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-2 } */\n+/* { dg-note \"expression evaluates to '9'\" \"\" { target c++ } .-3 } */\n #pragma omp allocate(okay1) align(128) allocator(ompx_gnu_pinned_mem_alloc)  /* Okay */\n-#pragma omp allocate(invalid2) align(128) allocator(ompx_gnu_pinned_bogus_2) /* { dg-error \"'allocator' clause requires a predefined allocator as 'invalid2' is static\" \"\" { xfail c++ } }  */\n-#pragma omp allocate(invalid3) align(128) allocator(ompx_gnu_pinned_bogus_3) /* { dg-error \"'allocator' clause requires a predefined allocator as 'invalid3' is static\" \"\" { xfail c++ } }  */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-4 } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-4 } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-4 } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-4 } */\n+#pragma omp allocate(invalid2) align(128) allocator(ompx_gnu_pinned_bogus_2) /* { dg-error \"'allocator' clause requires a predefined allocator as 'invalid2' is static\" \"\" { target c } }  */\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target c++ } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-2 } */\n+/* { dg-note \"expression evaluates to '199'\" \"\" { target c++ } .-3 } */\n+#pragma omp allocate(invalid3) align(128) allocator(ompx_gnu_pinned_bogus_3) /* { dg-error \"'allocator' clause requires a predefined allocator as 'invalid3' is static\" \"\" { target c } }  */\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target c++ } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-2 } */\n+/* { dg-note \"expression evaluates to '2001'\" \"\" { target c++ } .-3 } */\n+\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ } 0 } */\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-5.c b/gcc/testsuite/c-c++-common/gomp/allocate-5.c\nindex 09c8e4342f0..b34f9ea9227 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-5.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-5.c\n@@ -21,11 +21,11 @@ foo ()\n   omp_allocator_handle_t my_allocator = omp_default_mem_alloc;\n   int a, b;\n   static int c;\n-#pragma omp allocate (a)  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } } */\n-#pragma omp allocate (b) allocator(my_allocator)  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } } */\n+#pragma omp allocate (a)\n+#pragma omp allocate (b) allocator(my_allocator)\n #pragma omp allocate(c) align(32)\n   /* { dg-message \"'allocator' clause required for static variable 'c'\" \"\" { target c } .-1 } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-2 } */\n+  /* { dg-error \"'allocator' clause must be specified\" \"\" { target c++ } .-2 } */\n }\n \n void\n@@ -34,14 +34,14 @@ bar ()\n   int a, a2, b;\n   omp_allocator_handle_t my_allocator;\n #pragma omp allocate  /* { dg-error \"expected '\\\\(' before end of line\" } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n #pragma omp allocate allocator(my_allocator)  /* { dg-error \"expected '\\\\(' before 'allocator'\" } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n #pragma omp allocate(a) foo(my_allocator) /* { dg-error \"expected 'allocator'\" } */\n   /* { dg-error \"expected end of line before '\\\\(' token\" \"\" { target *-*-* } .-1 } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-2 } */\n-#pragma omp allocate(a2) allocator(b)  /* { dg-error \"'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'\" \"todo: cp/semantics.c\" { xfail c++ } } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+#pragma omp allocate(a2) allocator(b)\n+  /* { dg-error \"'allocator' clause expression has type 'int' rather than 'omp_allocator_handle_t'\" \"\" { target c } .-1 } */\n+  /* { dg-error \"invalid conversion from 'int' to 'omp_allocator_handle_t'\" \"\" { target c++ } .-2 } */\n+  /* We have diverging behavior here between c and c++ due to a difference in\n+     order of diagnostics, as well as diverging semantics, this should probably be unified.  */\n }\n \n \n@@ -50,32 +50,27 @@ align_test ()\n {\n   int i1,i2,i3,i4,i5,i6;\n   #pragma omp allocate(i1) allocator(omp_default_mem_alloc), align(32)\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n   #pragma omp allocate(i2) align ( 32 ),allocator(omp_default_mem_alloc)\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n   #pragma omp allocate(i3),allocator(omp_default_mem_alloc) align(32)\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n   #pragma omp allocate(i4) align ( 32 ) allocator(omp_default_mem_alloc)\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n   #pragma omp allocate(i5) allocator ( omp_high_bw_mem_alloc ), align ( 32 ) allocator(omp_default_mem_alloc)\n   /* { dg-error \"too many 'allocator' clauses\" \"\" { target *-*-* } .-1 } */\n   /* { dg-error \"expected end of line before '\\\\)' token\" \"\" { target *-*-* } .-2 } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-3 } */\n   #pragma omp allocate(i6) align ( 32 ), align(32) allocator(omp_default_mem_alloc)\n   /* { dg-error \"too many 'align' clauses\" \"\" { target *-*-* } .-1 } */\n   /* { dg-error \"expected end of line before '\\\\)' token\" \"\" { target *-*-* } .-2 } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-3 } */\n }\n \n void\n align_test2 ()\n {\n   int i, i2,i3;\n-  #pragma omp allocate(i) align (32.0)  /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+  #pragma omp allocate(i) align (32.0)\n+  /* { dg-error {could not convert '(?:\\(double\\))?3\\.2e\\+1l?' from 'double' to '(?:long )?unsigned int'} \"\" { target c++ } .-1 } */\n+  /* { dg-error {conversion from 'double' to '(?:long )?unsigned int' in a converted constant expression} \"\" { target c++ } .-2 } */\n+  /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" \"\" { target *-*-* } .-3 } */\n   #pragma omp allocate(i2) align ( 31 )  /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n   #pragma omp allocate(i3) align ( -32 )  /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" } */\n-  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+  /* { dg-bogus \"narrowing conversion of '-32' from\" \"\" { target c++ xfail c++11 } .-1 } */\n }\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-9.c b/gcc/testsuite/c-c++-common/gomp/allocate-9.c\nindex f37a11185f7..52bae8c9a6a 100644\n--- a/gcc/testsuite/c-c++-common/gomp/allocate-9.c\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-9.c\n@@ -1,93 +1,78 @@\n-typedef enum omp_allocator_handle_t\n-#if __cplusplus >= 201103L\n-: __UINTPTR_TYPE__\n-#endif\n-{\n-  omp_null_allocator = 0,\n-  omp_default_mem_alloc = 1,\n-  omp_large_cap_mem_alloc = 2,\n-  omp_const_mem_alloc = 3,\n-  omp_high_bw_mem_alloc = 4,\n-  omp_low_lat_mem_alloc = 5,\n-  omp_cgroup_mem_alloc = 6,\n-  omp_pteam_mem_alloc = 7,\n-  omp_thread_mem_alloc = 8,\n-  __ompx_last_mem_alloc = omp_thread_mem_alloc,\n-  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__\n-} omp_allocator_handle_t;\n-\n-\n-static int A[5] = {1,2,3,4,5};\n+#include \"allocate-allocator-handle.h\"\n+\n+static int A1[5] = {1,2,3,4,5}; /* { dg-line A_decl } */\n static int A2[5] = {1,2,3,4,5};\n static int A3[5] = {1,2,3,4,5};\n-static int A4[5] = {1,2,3,4,5};\n-static int A5[5] = {1,2,3,4,5};\n-int B, C, C2, D;\n+static int A4[5] = {1,2,3,4,5}; /* { dg-line A4_decl } */\n+static int A5[5] = {1,2,3,4,5}; /* { dg-line A5_decl } */\n+int B, C, C2, D; /* { dg-note \"declared here\" \"\" { xfail c++ } } */\n \n /* If the following fails because of added predefined allocators, please update\n+   - include/gomp-constants.h's GOMP_OMP_PREDEF_ALLOC_MAX or GOMP_OMPX_PREDEF_ALLOC_MAX\n    - c/c-parser.c's c_parser_omp_allocate\n    - fortran/openmp.cc's is_predefined_allocator\n    - libgomp/env.c's parse_allocator\n    - libgomp/libgomp.texi (document the new values - multiple locations)\n    + ensure that the memory-spaces are also up to date. */\n \n-#pragma omp allocate(A) align(32) allocator((omp_allocator_handle_t) 9) /* { dg-error \"'allocator' clause requires a predefined allocator as 'A' is static\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n-\n+#pragma omp allocate(A1) align(32) allocator((omp_allocator_handle_t) 9)\n+/* { dg-error \"'allocator' clause requires a predefined allocator as 'A1' is static\" \"\" { target c } .-1 } */\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target c++ } .-2 } */\n+/* { dg-message \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-3 } */\n+/* { dg-message \"expression evaluates to '9'\" \"\" { target c++ } .-4 } */\n+/* { dg-note \"'A1' declared here\" \"\" { target c++ xfail c++ } A_decl } */\n // typo in allocator name:\n #pragma omp allocate(A2) allocator(omp_low_latency_mem_alloc)\n /* { dg-error \"'omp_low_latency_mem_alloc' undeclared here \\\\(not in a function\\\\); did you mean 'omp_low_lat_mem_alloc'\\\\?\" \"\" { target c } .-1 } */\n /* { dg-error \"'omp_low_latency_mem_alloc' was not declared in this scope; did you mean 'omp_low_lat_mem_alloc'\\\\?\" \"\" { target c++ } .-2 } */\n /* { dg-error \"'allocator' clause required for static variable 'A2'\" \"\" { target c } .-3 } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-4 } */\n \n /* align be const multiple of 2 */\n #pragma omp allocate(A3) align(31) allocator(omp_default_mem_alloc) /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n \n-/* allocator missing (required as A is static) */\n-#pragma omp allocate(A4) align(32) /* { dg-error \"'allocator' clause required for static variable 'A4'\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+/* allocator missing (required as A4 is static) */\n+#pragma omp allocate(A4) align(32)\n+/* { dg-error \"'allocator' clause required for static variable 'A4'\" \"\" { target c } .-1 } */\n+/* { dg-error \"'allocator' clause must be specified\" \"\" { target c++ } .-2 } */\n+/* { dg-message \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-3 } */\n+/* { dg-note \"'A4' declared here\" \"\" { target c++ xfail c++ } A4_decl } */\n \n /* \"expression in the clause must be a constant expression that evaluates to one of the\n    predefined memory allocator values -> omp_low_lat_mem_alloc\"  */\n #pragma omp allocate(B) allocator((omp_allocator_handle_t) (omp_high_bw_mem_alloc+1)) align(32) /* OK: omp_low_lat_mem_alloc */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n \n #pragma omp allocate(C) allocator((omp_allocator_handle_t) 2) /* OK: omp_large_cap_mem_alloc */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n \n-#pragma omp allocate(A5) align(32) allocator(omp_null_allocator) /* { dg-error \"'allocator' clause requires a predefined allocator as 'A5' is static\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+#pragma omp allocate(A5) align(32) allocator(omp_null_allocator)\n+/* { dg-error \"'allocator' clause requires a predefined allocator as 'A5' is static\" \"\" { target c } .-1 } */\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target c++ } .-2 } */\n+/* { dg-message \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-3 } */\n+/* { dg-message \"expression evaluates to '0'\" \"\" { target c++ } .-4 } */\n+/* { dg-note \"'A5' declared here\" \"\" { target c++ xfail c++ } A5_decl } */\n \n #pragma omp allocate(C2) align(32) allocator(omp_large_cap_mem_alloc)\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n \n \n // allocate directive in same TU\n int f()\n {\n   #pragma omp allocate(D) align(32) allocator(omp_large_cap_mem_alloc) /* { dg-error \"'allocate' directive must be in the same scope as 'D'\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n-/* { dg-note \"declared here\" \"\" { target c } 25 } */\n-  return A[0];\n+  return A1[0];\n }\n \n int g()\n {\n-  int a2=1, b2=2;\n+  int a2=1, b2=2; /* { dg-line g_a2_b2_decl } */\n   #pragma omp allocate(a2)\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n-  #pragma omp allocate(a2)  /* { dg-error \"'a2' already appeared as list item in an 'allocate' directive\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+  #pragma omp allocate(a2) /* { dg-error \"'a2' already appeared as list item in an 'allocate' directive\" } */\n+/* { dg-note \"'a2' previously appeared here\" \"\" { target c++ } .-2 } */\n   {\n     int c2=3;\n     #pragma omp allocate(c2, b2) /* { dg-error \"'allocate' directive must be in the same scope as 'b2'\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n-/* { dg-note \"declared here\" \"\" { target c } .-9 } */\n+/* { dg-note \"declared here\" \"\" { target *-*-* xfail c++ } g_a2_b2_decl } */\n     return c2+a2+b2;\n   }\n }\n@@ -95,15 +80,20 @@ int g()\n int h(int q)\n {\n   #pragma omp allocate(q)  /* { dg-error \"function parameter 'q' may not appear as list item in an 'allocate' directive\" \"\" { xfail c++ } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+/* { dg-note \"parameter 'q' declared here\" \"\" { target c++ xfail c++ } .-3 } */\n   return q;\n }\n \n int\n k ()\n {\n-  static int var3 = 8;\n-  #pragma omp allocate(var3) allocator((omp_allocator_handle_t)-1L)  /* { dg-error \"'allocator' clause requires a predefined allocator as 'var3' is static\" \"\" { target c } } */\n-/* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } .-1 } */\n+  static int var3 = 8; /* { dg-note \"'var3' declared here\" \"\" { target c++ xfail c++ } } */\n+  #pragma omp allocate(var3) allocator((omp_allocator_handle_t)-1L)\n+  /* { dg-error \"'allocator' clause requires a predefined allocator as 'var3' is static\" \"\" { target c } .-1 } */\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target c++ } .-2 } */\n+  /* { dg-message \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { target c++ xfail c++ } .-3 } */\n+  /* { dg-message \"expression evaluates to '\\\\d+'\" \"\" { target c++ } .-4 } */\n   return var3;\n }\n+\n+/* { dg-bogus \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail c++ } 0 } */\ndiff --git a/gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h b/gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h\nnew file mode 100644\nindex 00000000000..1f94c303342\n--- /dev/null\n+++ b/gcc/testsuite/c-c++-common/gomp/allocate-allocator-handle.h\n@@ -0,0 +1,19 @@\n+typedef __UINTPTR_TYPE__ omp_uintptr_t;\n+\n+typedef enum omp_allocator_handle_t\n+#if __cplusplus >= 201103L\n+: omp_uintptr_t\n+#endif\n+{\n+  omp_null_allocator = 0,\n+  omp_default_mem_alloc = 1,\n+  omp_large_cap_mem_alloc = 2,\n+  omp_const_mem_alloc = 3,\n+  omp_high_bw_mem_alloc = 4,\n+  omp_low_lat_mem_alloc = 5,\n+  omp_cgroup_mem_alloc = 6,\n+  omp_pteam_mem_alloc = 7,\n+  omp_thread_mem_alloc = 8,\n+  __ompx_last_mem_alloc = omp_thread_mem_alloc,\n+  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__\n+} omp_allocator_handle_t;\ndiff --git a/gcc/testsuite/c-c++-common/gomp/directive-1.c b/gcc/testsuite/c-c++-common/gomp/directive-1.c\nindex 34194865d10..19d9290daf8 100644\n--- a/gcc/testsuite/c-c++-common/gomp/directive-1.c\n+++ b/gcc/testsuite/c-c++-common/gomp/directive-1.c\n@@ -20,7 +20,6 @@ foo (void)\n   int i, k = 0, l = 0;\n   #pragma omp allocate, (i)\t\t\t/* { dg-error \"expected '\\\\\\(' before ',' token\" } */\n \t\t\t\t\t\t/* { dg-error \"expected end of line before ',' token\" \"\" { target c++ } .-1 } */\n-\t\t\t\t\t\t/* { dg-message \"not yet supported\" \"\" { target c++ } .-2 } */\n   #pragma omp critical, (bar)\t\t\t/* { dg-error \"expected an OpenMP clause before '\\\\\\(' token\" } */\n   ;\n   #pragma omp flush, (k, l)\t\t\t/* { dg-error \"expected '\\\\\\(' or end of line before ',' token\" \"\" { target c } } */\ndiff --git a/gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c b/gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c\nindex df82cbbcba9..53c497dc655 100644\n--- a/gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c\n+++ b/gcc/testsuite/c-c++-common/gomp/uses_allocators-1.c\n@@ -22,7 +22,7 @@ f (omp_allocator_handle_t my_alloc)\n   #pragma omp target\n   {\n     int a; /* { dg-error \"'my_alloc' in 'allocator' clause inside a target region must be specified in an 'uses_allocators' clause on the 'target' directive\" \"not yet implemented\" { xfail *-*-* } } */\n-    #pragma omp allocate(a) allocator(my_alloc) /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } }  */\n+    #pragma omp allocate(a) allocator(my_alloc)\n     a  = 5;\n     void *prt = omp_alloc(32, my_alloc);\n     #pragma omp parallel allocate(allocator(my_alloc) : a) firstprivate(a) /* { dg-error \"allocator 'my_alloc' in 'allocate' clause inside a target region must be specified in an 'uses_allocators' clause on the 'target' directive\" \"not yet implemented\" { xfail *-*-* } } */\n@@ -37,7 +37,7 @@ g (omp_allocator_handle_t my_alloc)\n   #pragma omp target uses_allocators(my_alloc)\n   {\n     int a;\n-    #pragma omp allocate(a) allocator(my_alloc)  /* { dg-message \"sorry, unimplemented: '#pragma omp allocate' not yet supported\" \"\" { target c++ } }  */\n+    #pragma omp allocate(a) allocator(my_alloc)\n     a  = 5;\n     void *prt = omp_alloc(32, my_alloc);\n     #pragma omp parallel allocate(allocator(my_alloc) : a) firstprivate(a)\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-10.C b/gcc/testsuite/g++.dg/gomp/allocate-10.C\nnew file mode 100644\nindex 00000000000..e0f3bcc1780\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-10.C\n@@ -0,0 +1,115 @@\n+/* { dg-do compile { target c++17 } } */\n+#include \"allocate-allocator-handle.h\"\n+\n+/* Invalid allocator clause.  */\n+\n+template<auto Alloc>\n+void auto_nttp_allocator()\n+{\n+  int a;\n+  #pragma omp allocate(a) allocator(Alloc) /* { dg-error \"invalid conversion from 'int' to 'omp_allocator_handle_t'\" } */\n+}\n+\n+template<auto Alloc>\n+void auto_nttp_allocator_uninstantiated()\n+{\n+  int a;\n+  #pragma omp allocate(a) allocator(Alloc)\n+}\n+\n+#define DEFINITELY_NOT_PREDEFINED static_cast<omp_allocator_handle_t>(1024)\n+\n+void instantiate_auto_nttp_allocator()\n+{\n+  auto_nttp_allocator<omp_default_mem_alloc>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_allocator<DEFINITELY_NOT_PREDEFINED>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_allocator<1>(); /* { dg-message \"required from here\" } */\n+}\n+\n+#undef DEFINITELY_NOT_PREDEFINED\n+\n+template<auto Alloc>\n+void auto_nttp_allocator_static_0()\n+{\n+  static int a;\n+  #pragma omp allocate(a) allocator(Alloc)\n+  /* { dg-error \"invalid conversion from 'int' to 'omp_allocator_handle_t'\" \"\" { target *-*-* } .-1 } */\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { xfail *-*-* } .-2 } */\n+}\n+\n+template<auto Alloc>\n+void auto_nttp_allocator_static_1()\n+{\n+  static int a;\n+  #pragma omp allocate(a) allocator(Alloc) /* { dg-error \"invalid conversion from 'int' to 'omp_allocator_handle_t'\" } */\n+}\n+\n+template<auto Alloc>\n+void auto_nttp_allocator_static_2()\n+{\n+  static int a;\n+  #pragma omp allocate(a) allocator(Alloc)\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { xfail *-*-* } .-1 } */\n+}\n+\n+template<auto Alloc>\n+void auto_nttp_allocator_static_uninstantiated()\n+{\n+  static int a;\n+  #pragma omp allocate(a) allocator(Alloc)\n+}\n+\n+#define DEFINITELY_NOT_PREDEFINED static_cast<omp_allocator_handle_t>(1024)\n+\n+void instantiate_auto_nttp_allocator_static()\n+{\n+  auto_nttp_allocator_static_0<omp_default_mem_alloc>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_allocator_static_0<1>(); /* { dg-message \"required from here\" } */\n+  auto_nttp_allocator_static_0<DEFINITELY_NOT_PREDEFINED>(); /* { dg-message \"required from here\" } */\n+\n+  auto_nttp_allocator_static_1<omp_default_mem_alloc>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_allocator_static_1<1>(); /* { dg-message \"required from here\" } */\n+\n+  auto_nttp_allocator_static_2<omp_default_mem_alloc>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_allocator_static_2<DEFINITELY_NOT_PREDEFINED>(); /* { dg-message \"required from here\" } */\n+}\n+\n+#undef DEFINITELY_NOT_PREDEFINED\n+\n+/* { dg-bogus \"'allocator' clause requires a constant predefined allocator\" \"\" { xfail *-*-* } 0 } */\n+\n+/* Invalid align clause */\n+\n+template<auto Align>\n+void auto_nttp_align_uninstantiated()\n+{\n+  int a;\n+  #pragma omp allocate(a) align(Align)\n+}\n+\n+template<auto Align>\n+void auto_nttp_align_0()\n+{\n+  int a;\n+  #pragma omp allocate(a) align(Align) /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" \"\" { xfail *-*-* } } */\n+}\n+\n+template<auto Align>\n+void auto_nttp_align_1()\n+{\n+  int a;\n+  #pragma omp allocate(a) align(Align)\n+  /* { dg-error \"'align' clause argument needs to be positive constant power of two integer expression\" \"\" { xfail *-*-* } .-1 } */\n+  /* { dg-error {could not convert 'nullptr' from 'std::nullptr_t' to '(?:long )?unsigned int'} \"\" { target *-*-* } .-2 } */\n+}\n+\n+void instantiate_auto_nttp_align()\n+{\n+  auto_nttp_align_0<32>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_align_0<42>(); /* { dg-message \"required from here\" } */\n+\n+  auto_nttp_align_1<32>(); /* { dg-bogus \"required from here\" } */\n+  auto_nttp_align_1<nullptr>(); /* { dg-message \"required from here\" } */\n+}\n+\n+/* { dg-bogus \"'align' clause argument needs to be positive constant power of two integer expression\" \"\" { xfail *-*-* } 0 } */\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-11.C b/gcc/testsuite/g++.dg/gomp/allocate-11.C\nnew file mode 100644\nindex 00000000000..d9bc1de3ef8\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-11.C\n@@ -0,0 +1,18 @@\n+/* Just a silly ICE I came across by accident, easy fix, might be a problem\n+   with lookup_name but I'm not certain.\n+\n+   For some reason, the floating 'a;' breaks lookup_name in tsubst_stmt during\n+   substitution of the allocate directive, despite it being found no problem\n+   during parsing of the allocate directive's var list.\n+   I don't have time to investigate it further so I'm just going to fix it\n+   by checking for NULL_TREE on the return of lookup_name.  */\n+   \n+template<typename>\n+void f()\n+{\n+    a; /* { dg-error \"'a' was not declared in this scope\" } */\n+    int a = 42;\n+    #pragma omp allocate(a)\n+}\n+template void f<void>();\n+\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-12.C b/gcc/testsuite/g++.dg/gomp/allocate-12.C\nnew file mode 100644\nindex 00000000000..53fa5712f50\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-12.C\n@@ -0,0 +1,38 @@\n+/* { dg-do compile { target c++14 } } */\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+\n+/* Ensure allocate directive does not break attributes on a decl (and vice-versa).  */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\(64,\" 2 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\(32,\" 2 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\(16,\" 2 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\(8,\" 2 \"gimple\" } } */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_free\" 8 \"gimple\" } } */\n+\n+template<unsigned Align>\n+int test0 ()\n+{\n+  int a [[deprecated]];\n+  #pragma omp allocate(a) align(Align)\n+  return a; /* { dg-warning  \"'a' is deprecated\" } */\n+}\n+\n+template int test0<64u>();\n+template int test0<32u>();\n+template int test0<16u>();\n+template int test0<8u>();\n+\n+\n+template<unsigned Align>\n+int test1 ()\n+{\n+  int a [[deprecated]];\n+  #pragma omp allocate(a) align(Align)\n+  return a; /* { dg-warning  \"'a' is deprecated\" } */\n+}\n+\n+template int test1<8u>();\n+template int test1<16u>();\n+template int test1<32u>();\n+template int test1<64u>();\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-13.C b/gcc/testsuite/g++.dg/gomp/allocate-13.C\nnew file mode 100644\nindex 00000000000..ffc51b1d2c8\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-13.C\n@@ -0,0 +1,28 @@\n+/* { dg-do compile { target c++11 } } */\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+\n+/* Misc valid constexpr expressions in a clause.\n+   TODO: Really needs more cases.  */\n+\n+/* Not constexpr, invalid value, not called.  */\n+template<typename T>\n+int get_align(T) { return 42; }\n+\n+template<typename T>\n+int dependent_call_adl()\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(get_align(T{}))\n+  return a;\n+}\n+\n+namespace foo\n+{\n+  struct S {};\n+\n+  constexpr int get_align(S) { return 32; }\n+}\n+\n+template int dependent_call_adl<foo::S>();\n+/* { dg-final { scan-tree-dump \"__builtin_GOMP_alloc \\\\(32,\" \"gimple\" } } */\n+/* { dg-final { scan-tree-dump \"__builtin_GOMP_free\" \"gimple\" } } */\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-5.C b/gcc/testsuite/g++.dg/gomp/allocate-5.C\nnew file mode 100644\nindex 00000000000..724dd980138\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-5.C\n@@ -0,0 +1,411 @@\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+/* { dg-skip-if \"\" { ilp32 } } */\n+\n+#include \"allocate-allocator-handle.h\"\n+\n+/* All cases are valid and should work.  */\n+\n+/* Note: the OpenMP allocate directive is not supposed to change the alignof\n+   of expr a, it was decided to have too many edge cases.  The static asserts\n+   in these cases correctly test that it remains untouched.\n+   \n+   Note: cases that take omp_default_mem_alloc and have no align clause elide\n+   the GOMP_alloc/GOMP_free transformations.  */\n+\n+/* 86 total cases.  */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc\" 86 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_free\" 86 \"gimple\" } } */\n+\n+/* FIXME: This almost certainly needs tweaks for systems where alignof(int)\n+   is not 4.  */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(32,\" 31 \"gimple\" } } */\n+\n+/* 9 * 3 = 27 cases scanned here.  */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(8,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(16,\" 3 \"gimple\" } } */\n+/* Align==32 scanned above.  */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(64,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(128,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(256,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(512,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(1024,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(2048,\" 3 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(4096,\" 3 \"gimple\" } } */\n+\n+/* The align argument is at least alignof(int) (4) in all cases, thus we can subtract\n+   the cases where align >= 8 from the total cases to determine how many cases\n+   there are where align == 4.\n+   86 - (27 + 31) = 28.\n+   Please update this math if changes are made to this test so everything\n+   is (somewhat) coherent.  */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(4,\" 28 \"gimple\" } } */\n+\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 0B\\\\\\)\" 29 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 1\\\\\\)\" 22 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 2\\\\\\)\" 31 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 3\\\\\\)\" 2 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 4\\\\\\)\" 2 \"gimple\" } } */\n+\n+struct S0 {\n+  int _v;\n+  S0(int v) : _v(v) {}\n+  operator int() const { return 42; }\n+};\n+\n+struct S1 {\n+  int _v[2];\n+  S1(int v) : _v() {\n+    int *end = _v + sizeof(_v) / sizeof(*_v);\n+    for (int *it = _v; it != end; ++it)\n+      *it = v;\n+  }\n+  operator int() const { return 42; }\n+};\n+\n+/**********************\n+ * dependent variable *\n+ **********************/\n+\n+template<typename T>\n+T dep_local()\n+{\n+  T a = 42;\n+  #pragma omp allocate(a)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(T));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+T dep_local_align()\n+{\n+  T a = 42;\n+  #pragma omp allocate(a) align(32)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(T));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+T dep_local_alloc_0()\n+{\n+  T a = 42;\n+  #pragma omp allocate(a) allocator(omp_default_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(T));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+T dep_local_alloc_1()\n+{\n+  T a = 42;\n+  #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(T));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+T dep_local_align_alloc_0()\n+{\n+  T a = 42;\n+  #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(T));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+T dep_local_align_alloc_1()\n+{\n+  T a = 42;\n+  #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(T));\n+  #endif\n+  return a;\n+}\n+\n+/* 5 cases per expansion, dep_local_alloc_0 uses omp_default_mem_alloc\n+   without an align clause.\n+   2 allocator==0B,\n+   1 allocator==1 (because 1 case elided),\n+   2 allocator==2 cases per expansion.\n+\n+   3 align==32 cases per expansion.  */\n+#define INSTANTIATE_ALL_WITH_T(type)\t\t\\\n+  do {\t\t\t\t\t\t\\\n+    type v0 = dep_local<type>();\t\t\\\n+    type v1 = dep_local_align<type>();\t\t\\\n+    type v2 = dep_local_alloc_0<type>();\t\\\n+    type v3 = dep_local_alloc_1<type>();\t\\\n+    type v4 = dep_local_align_alloc_0<type>();\t\\\n+    type v5 = dep_local_align_alloc_1<type>();\t\\\n+    static_cast<void>(v0);\t\t\t\\\n+    static_cast<void>(v1);\t\t\t\\\n+    static_cast<void>(v2);\t\t\t\\\n+    static_cast<void>(v3);\t\t\t\\\n+    static_cast<void>(v4);\t\t\t\\\n+    static_cast<void>(v5);\t\t\t\\\n+  } while (false)\n+\n+/* 4 * 5 cases, 20.\n+   4 * 2 allocator==0B cases, 8.\n+   4 * 1 allocator==1 cases, 4.\n+   4 * 2 allocator==2 cases, 8.\n+   4 * 3 align==32 cases, 12.  */\n+void instantiate_dep_tests()\n+{\n+  INSTANTIATE_ALL_WITH_T(int);\n+  INSTANTIATE_ALL_WITH_T(float);\n+  INSTANTIATE_ALL_WITH_T(S0);\n+  INSTANTIATE_ALL_WITH_T(S1);\n+}\n+\n+#undef INSTANTIATE_ALL_WITH_T\n+\n+/**********************\n+ * template parameter *\n+ **********************/\n+\n+template<typename T>\n+int template_parm(T)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+int template_parm_align(T)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+int template_parm_alloc_0(T)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) allocator(omp_default_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+int template_parm_alloc_1(T)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+int template_parm_align_alloc_0(T)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<typename T>\n+int template_parm_align_alloc_1(T)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+/* 5 cases per expansion, template_parm_alloc_0 uses omp_default_mem_alloc\n+   without an align clause.\n+   2 allocator==0B,\n+   1 allocator==1 (because 1 case elided),\n+   2 allocator==2 cases per expansion.\n+\n+   3 align==32 cases per expansion.  */\n+#define INSTANTIATE_ALL_WITH_T(type)\t\t\\\n+  do {\t\t\t\t\t\t\\\n+    type a = 42;\t\t\t\t\\\n+    int v0 = template_parm(a);\t\t\t\\\n+    int v1 = template_parm_align(a);\t\t\\\n+    int v2 = template_parm_alloc_0(a);\t\t\\\n+    int v3 = template_parm_alloc_1(a);\t\t\\\n+    int v4 = template_parm_align_alloc_0(a);\t\\\n+    int v5 = template_parm_align_alloc_1(a);\t\\\n+    static_cast<void>(v0);\t\t\t\\\n+    static_cast<void>(v1);\t\t\t\\\n+    static_cast<void>(v2);\t\t\t\\\n+    static_cast<void>(v3);\t\t\t\\\n+    static_cast<void>(v4);\t\t\t\\\n+    static_cast<void>(v5);\t\t\t\\\n+  } while (false)\n+\n+/* 4 * 5 cases, 20.\n+   4 * 2 allocator==0B cases, 8.\n+   4 * 1 allocator==1 cases, 4.\n+   4 * 2 allocator==2 cases, 8.\n+   4 * 3 align==32 cases, 12.  */\n+void instantiate_template_parm_tests()\n+{\n+  INSTANTIATE_ALL_WITH_T(int);\n+  INSTANTIATE_ALL_WITH_T(float);\n+  INSTANTIATE_ALL_WITH_T(S0);\n+  INSTANTIATE_ALL_WITH_T(S1);\n+}\n+\n+#undef INSTANTIATE_ALL_WITH_T\n+\n+/*************************************\n+ * non-type template parameter align *\n+ *************************************/\n+\n+template<int Align>\n+int nttp_align()\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(Align)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<int Align>\n+int nttp_align_alloc_0()\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(Align) allocator(omp_default_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<int Align>\n+int nttp_align_alloc_1()\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(Align) allocator(omp_large_cap_mem_alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+/* 3 cases per expansion.\n+   1 allocator==0B, 1 allocator==1, 1 allocator==2 cases per expansion.\n+   3 align==value cases per expansion.  */\n+#define INSTANTIATE_ALL_WITH_V(value)\t\t\\\n+  do {\t\t\t\t\t\t\\\n+    int v0 = nttp_align<value>();\t\t\\\n+    int v1 = nttp_align_alloc_0<value>();\t\\\n+    int v2 = nttp_align_alloc_1<value>();\t\\\n+    static_cast<void>(v0);\t\t\t\\\n+    static_cast<void>(v1);\t\t\t\\\n+    static_cast<void>(v2);\t\t\t\\\n+  } while (false)\n+\n+/* 13 * 3 cases, 39.\n+   13 * allocator==0B cases.\n+   13 * allocator==1 cases.\n+   13 * allocator==2 cases.  */\n+void instantiate_nttp_align_tests()\n+{\n+  /* Minimum align for int is 4, 3 * 3 alloc=4, 9 alloc=4.  */\n+  INSTANTIATE_ALL_WITH_V(1);\n+  INSTANTIATE_ALL_WITH_V(2);\n+  INSTANTIATE_ALL_WITH_V(4);\n+  /* 3 alloc=N for the rest.  */\n+  INSTANTIATE_ALL_WITH_V(8);\n+  INSTANTIATE_ALL_WITH_V(16);\n+  INSTANTIATE_ALL_WITH_V(32);\n+  INSTANTIATE_ALL_WITH_V(64);\n+  INSTANTIATE_ALL_WITH_V(128);\n+  INSTANTIATE_ALL_WITH_V(256);\n+  INSTANTIATE_ALL_WITH_V(512);\n+  INSTANTIATE_ALL_WITH_V(1024);\n+  INSTANTIATE_ALL_WITH_V(2048);\n+  INSTANTIATE_ALL_WITH_V(4096);\n+}\n+\n+#undef INSTANTIATE_ALL_WITH_V\n+\n+\n+template<omp_allocator_handle_t Alloc>\n+int nttp_alloc()\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) allocator(Alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+template<omp_allocator_handle_t Alloc>\n+int nttp_alloc_align()\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32) allocator(Alloc)\n+  #if __cplusplus >= 201103L\n+  static_assert(alignof(a) == alignof(int));\n+  #endif\n+  return a;\n+}\n+\n+/* 2 cases per expansion.\n+   2 allocator=value per expansion.\n+   1 alloc=32 per expansion.  */\n+#define INSTANTIATE_ALL_WITH_V(value)\t\t\\\n+  do {\t\t\t\t\t\t\\\n+    int v0 = nttp_alloc<value>();\t\t\\\n+    int v1 = nttp_alloc_align<value>();\t\t\\\n+    static_cast<void>(v0);\t\t\t\\\n+    static_cast<void>(v1);\t\t\t\\\n+  } while (false)\n+\n+/* 4 * 2 cases, 8,\n+   -1 omp_default_mem_alloc without align, 7 cases.\n+   1 * allocator==1 (1 elided),\n+   2 * allocator==2,\n+   2 * allocator==3,\n+   2 * allocator==4 cases.\n+   4 * 1 alloc=32 cases, 4.  */\n+void instantiate_nttp_alloc_tests()\n+{\n+  INSTANTIATE_ALL_WITH_V(omp_default_mem_alloc);\n+  INSTANTIATE_ALL_WITH_V(omp_large_cap_mem_alloc);\n+  INSTANTIATE_ALL_WITH_V(omp_const_mem_alloc);\n+  INSTANTIATE_ALL_WITH_V(omp_high_bw_mem_alloc);\n+}\n+\n+#undef INSTANTIATE_ALL_WITH_V\n+\n+/* We are probably missing quite a few cases here.\n+   Missing cases with a param in allocator clause.  */\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-6.C b/gcc/testsuite/g++.dg/gomp/allocate-6.C\nnew file mode 100644\nindex 00000000000..eb0c78a24db\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-6.C\n@@ -0,0 +1,392 @@\n+/* { dg-do compile { target c++11 } } */\n+/* { dg-skip-if \"\" { ilp32 } } */\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+\n+#include \"allocate-allocator-handle.h\"\n+\n+/* Valid uses of lambda captures in an allocator clause.  */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc\" 200 \"gimple\" } } */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_free\" 200 \"gimple\" } } */\n+\n+template<typename, typename>\n+struct is_same { static constexpr bool value = false; };\n+\n+template<typename T>\n+struct is_same<T, T> { static constexpr bool value = true; };\n+\n+struct S0 {\n+  int _v;\n+  S0(int v) : _v(v) {}\n+  operator int() const { return 42; }\n+};\n+\n+struct S1 {\n+  int _v[2];\n+  S1(int v) : _v() {\n+    int *end = _v + sizeof(_v) / sizeof(*_v);\n+    for (int *it = _v; it != end; ++it)\n+      *it = v;\n+  }\n+  operator int() const { return 42; }\n+};\n+\n+/* Suppresses int/float cases from being optimized out, I'm not sure if this is\n+   going to be sufficient for all cases though.  */\n+int (*prevent_optimization)() = nullptr;\n+\n+#define BLANK_ARGUMENT\n+\n+/* Capturing with an initiaizer was added in C++14  */\n+#if __cplusplus >= 201402L\n+\n+#define CAPTURE_LITERAL_INIT_LAMBDA(type)\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+  auto capture_literal_init = [alloc = omp_default_mem_alloc](){\t\\\n+    type a = prevent_optimization();\t\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc)\")\t\t\t\t\\\n+    return a;\t\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\t\\\n+  auto result = capture_literal_init();\t\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result)>::value);\t\t\\\n+} while (false)\n+\n+void test_capture_literal_init_nondep_lambdas()\n+{\n+  /* 4 cases */\n+  CAPTURE_LITERAL_INIT_LAMBDA(int);\n+  CAPTURE_LITERAL_INIT_LAMBDA(float);\n+  CAPTURE_LITERAL_INIT_LAMBDA(S0);\n+  CAPTURE_LITERAL_INIT_LAMBDA(S1);\n+}\n+\n+/* 1 case per instantiation */\n+template<typename T>\n+void test_capture_literal_init_dependent_lambdas()\n+{\n+  CAPTURE_LITERAL_INIT_LAMBDA(T);\n+}\n+/* 4 cases */\n+template void test_capture_literal_init_dependent_lambdas<int>();\n+template void test_capture_literal_init_dependent_lambdas<float>();\n+template void test_capture_literal_init_dependent_lambdas<S0>();\n+template void test_capture_literal_init_dependent_lambdas<S1>();\n+\n+#undef CAPTURE_LITERAL_INIT_LAMBDA\n+\n+\n+\n+#define EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, capture)\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+  auto explicit_capture_with_init = [opt_tok alloc = capture](){\t\\\n+    type a = prevent_optimization();\t\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc)\")\t\t\t\t\\\n+    return a;\t\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\t\\\n+  auto result = explicit_capture_with_init();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result)>::value);\t\t\\\n+} while (false)\n+\n+/* 4 cases per expansion */\n+#define TEST_LAMBDAS_WITH_TYPE(type, opt_tok)\t\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+  EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc);\t\t\\\n+  EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc_ref);\t\t\\\n+  EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc_param);\t\\\n+  EXPLICIT_CAPTURE_WITH_INIT_LAMBDA(type, opt_tok, alloc_ref_param);\t\\\n+} while (false)\n+\n+/* 4*4 = 16 cases */\n+void test_capture_by_value_with_init_nondep_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t    omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;\n+  TEST_LAMBDAS_WITH_TYPE(int, BLANK_ARGUMENT);\n+  TEST_LAMBDAS_WITH_TYPE(float, BLANK_ARGUMENT);\n+  TEST_LAMBDAS_WITH_TYPE(S0, BLANK_ARGUMENT);\n+  TEST_LAMBDAS_WITH_TYPE(S1, BLANK_ARGUMENT);\n+}\n+\n+/* 4*4 = 16 cases */\n+void test_capture_by_ref_with_init_nondep_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t  omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  TEST_LAMBDAS_WITH_TYPE(int, &);\n+  TEST_LAMBDAS_WITH_TYPE(float, &);\n+  TEST_LAMBDAS_WITH_TYPE(S0, &);\n+  TEST_LAMBDAS_WITH_TYPE(S1, &);\n+}\n+\n+/* 4 cases per instantiation */\n+template<typename T>\n+void test_capture_by_value_with_init_dependent_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t       omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;\n+  TEST_LAMBDAS_WITH_TYPE(T, BLANK_ARGUMENT);\n+}\n+/* 4*4 = 16 cases */\n+template void test_capture_by_value_with_init_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_capture_by_value_with_init_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_capture_by_value_with_init_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_capture_by_value_with_init_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+\n+/* 4 cases per instantiation */\n+template<typename T>\n+void test_capture_by_ref_with_init_dependent_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t     omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  TEST_LAMBDAS_WITH_TYPE(T, &);\n+}\n+/* 4*4 = 16 cases */\n+template void test_capture_by_ref_with_init_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_capture_by_ref_with_init_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_capture_by_ref_with_init_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_capture_by_ref_with_init_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+\n+#undef EXPLICIT_CAPTURE_WITH_INIT_LAMBDA\n+#undef TEST_LAMBDAS_WITH_TYPE\n+\n+#else\n+/* There are 4 + 4 + 16 + 16 + 16 + 16 cases in the above, we replicate that\n+   many so our count in dg-final isn't wrong in C++11 mode.\n+   The counter isn't required but it doesn't hurt and might make it easier\n+   to inspect the asm if we need to, that's the goal anyway.  */\n+\n+#define DUMMY_ALLOC(counter)\t\t\t\\\n+do {\t\t\t\t\t\t\\\n+  auto dummy_ ##counter = [alloc](){\t\t\\\n+    int a = prevent_optimization();\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc)\")\t\\\n+    return a;\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\\\n+  auto result = dummy_ ##counter();\t\t\\\n+} while (false)\n+\n+#define DUMMY_X4(counter) \t\\\n+do {\t\t\t\t\\\n+  DUMMY_ALLOC(counter ##A);\t\\\n+  DUMMY_ALLOC(counter ##B);\t\\\n+  DUMMY_ALLOC(counter ##C);\t\\\n+  DUMMY_ALLOC(counter ##D);\t\\\n+} while (false)\n+\n+void dummy_cases(omp_allocator_handle_t alloc)\n+{\n+  /* 4x1 for test_capture_literal_init_nondep_lambdas */\n+  DUMMY_X4(0);\n+  /* 4x1 for test_capture_literal_init_dependent_lambdas */\n+  DUMMY_X4(1);\n+  /* 4x4 for test_capture_by_value_with_init_nondep_lambdas */\n+  DUMMY_X4(2);\n+  DUMMY_X4(3);\n+  DUMMY_X4(4);\n+  DUMMY_X4(5);\n+  /* 4x4 for test_capture_by_value_with_init_dependent_lambdas */\n+  DUMMY_X4(6);\n+  DUMMY_X4(7);\n+  DUMMY_X4(8);\n+  DUMMY_X4(9);\n+  /* 4x4 for test_capture_by_ref_with_init_dependent_lambdas */\n+  DUMMY_X4(9);\n+  DUMMY_X4(10);\n+  DUMMY_X4(11);\n+  DUMMY_X4(12);\n+  /* 4x4 for test_capture_by_ref_with_init_nondep_lambdas */\n+  DUMMY_X4(13);\n+  DUMMY_X4(14);\n+  DUMMY_X4(15);\n+  DUMMY_X4(16);\n+}\n+\n+#undef DUMMY_ALLOC\n+#undef DUMMY_X4\n+\n+#endif\n+\n+\n+#define EXPLICIT_CAPTURE_LAMBDAS(type, opt_tok)\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\\\n+  auto explicit_capture0 = [opt_tok alloc](){\t\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc)\")\t\t\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result0 = explicit_capture0();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result0)>::value);\t\\\n+\t\t\t\t\t\t\t\t\\\n+  auto explicit_capture1 = [opt_tok alloc_ref](){\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc_ref)\")\t\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result1 = explicit_capture1();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result1)>::value);\t\\\n+\t\t\t\t\t\t\t\t\\\n+  auto explicit_capture2 = [opt_tok alloc_param](){\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc_param)\")\t\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result2 = explicit_capture2();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result2)>::value);\t\\\n+\t\t\t\t\t\t\t\t\\\n+  auto explicit_capture3 = [opt_tok alloc_ref_param](){\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc_ref_param)\")\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result3 = explicit_capture3();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result3)>::value);\t\\\n+} while (false)\n+\n+void test_explicit_capture_by_value_nondep_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t   omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  EXPLICIT_CAPTURE_LAMBDAS(int, BLANK_ARGUMENT);\n+  EXPLICIT_CAPTURE_LAMBDAS(float, BLANK_ARGUMENT);\n+  EXPLICIT_CAPTURE_LAMBDAS(S0, BLANK_ARGUMENT);\n+  EXPLICIT_CAPTURE_LAMBDAS(S1, BLANK_ARGUMENT);\n+}\n+\n+void test_explicit_capture_by_ref_nondep_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  EXPLICIT_CAPTURE_LAMBDAS(int, &);\n+  EXPLICIT_CAPTURE_LAMBDAS(float, &);\n+  EXPLICIT_CAPTURE_LAMBDAS(S0, &);\n+  EXPLICIT_CAPTURE_LAMBDAS(S1, &);\n+}\n+\n+template<typename T>\n+void test_explicit_capture_by_value_dependent_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t      omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  EXPLICIT_CAPTURE_LAMBDAS(T, BLANK_ARGUMENT);\n+}\n+template void test_explicit_capture_by_value_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_explicit_capture_by_value_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_explicit_capture_by_value_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_explicit_capture_by_value_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+\t\t\t\t\t\t      \n+\n+template<typename T>\n+void test_explicit_capture_by_ref_dependent_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t    omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  EXPLICIT_CAPTURE_LAMBDAS(T, &);\n+}\n+template void test_explicit_capture_by_ref_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_explicit_capture_by_ref_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_explicit_capture_by_ref_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_explicit_capture_by_ref_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+\n+#undef EXPLICIT_CAPTURE_LAMBDA\n+#undef TEST_LAMBDAS_WITH_TYPE\n+\n+\n+#define DEFAULT_CAPTURE_LAMBDAS(type, capture)\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\\\n+  auto default_capture0 = [capture](){\t\t\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc)\")\t\t\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result0 = default_capture0();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result0)>::value);\t\\\n+\t\t\t\t\t\t\t\t\\\n+  auto default_capture1 = [capture](){\t\t\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc_ref)\")\t\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result1 = default_capture1();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result1)>::value);\t\\\n+\t\t\t\t\t\t\t\t\\\n+  auto default_capture2 = [capture](){\t\t\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc_param)\")\t\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result2 = default_capture2();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result2)>::value);\t\\\n+\t\t\t\t\t\t\t\t\\\n+  auto default_capture3 = [capture](){\t\t\t\t\\\n+    type a = prevent_optimization();\t\t\t\t\\\n+    _Pragma(\"omp allocate(a) allocator(alloc_ref_param)\")\t\\\n+    return a;\t\t\t\t\t\t\t\\\n+  };\t\t\t\t\t\t\t\t\\\n+  auto result3 = default_capture3();\t\t\t\t\\\n+  static_assert(is_same<type, decltype(result3)>::value);\t\\\n+} while (false)\n+\n+\n+\n+void test_default_capture_by_value_nondep_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t  omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  DEFAULT_CAPTURE_LAMBDAS(int, =);\n+  DEFAULT_CAPTURE_LAMBDAS(float, =);\n+  DEFAULT_CAPTURE_LAMBDAS(S0, =);\n+  DEFAULT_CAPTURE_LAMBDAS(S1, =);\n+}\n+\n+void test_default_capture_by_ref_nondep_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\tomp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  DEFAULT_CAPTURE_LAMBDAS(int, &);\n+  DEFAULT_CAPTURE_LAMBDAS(float, &);\n+  DEFAULT_CAPTURE_LAMBDAS(S0, &);\n+  DEFAULT_CAPTURE_LAMBDAS(S1, &);\n+}\n+\n+template<typename T>\n+void test_default_capture_by_value_dependent_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t     omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  DEFAULT_CAPTURE_LAMBDAS(T, =);\n+}\n+template void test_default_capture_by_value_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_default_capture_by_value_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_default_capture_by_value_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_default_capture_by_value_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+\n+template<typename T>\n+void test_default_capture_by_ref_dependent_lambdas(omp_allocator_handle_t alloc_param,\n+\t\t\t\t\t\t   omp_allocator_handle_t& alloc_ref_param)\n+{\n+  omp_allocator_handle_t alloc = omp_default_mem_alloc;\n+  omp_allocator_handle_t& alloc_ref = alloc;  \n+  DEFAULT_CAPTURE_LAMBDAS(T, &);\n+}\n+template void test_default_capture_by_ref_dependent_lambdas<int>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_default_capture_by_ref_dependent_lambdas<float>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_default_capture_by_ref_dependent_lambdas<S0>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+template void test_default_capture_by_ref_dependent_lambdas<S1>(omp_allocator_handle_t, omp_allocator_handle_t&);\n+\n+#undef DEFAULT_CAPTURE_LAMBDA\n+#undef TEST_LAMBDAS_WITH_TYPE\n+\n+/* Potential missing cases: captures that are type dependent, mutable lambdas.  */\n+\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-7.C b/gcc/testsuite/g++.dg/gomp/allocate-7.C\nnew file mode 100644\nindex 00000000000..01fc5b84014\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-7.C\n@@ -0,0 +1,222 @@\n+/* { dg-do compile { target c++20 } } */\n+/* { dg-skip-if \"\" { ilp32 } } */\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+\n+#include \"allocate-allocator-handle.h\"\n+\n+\n+/* Valid C++20 cases.\n+\n+   Note: cases that take omp_default_mem_alloc and have no align clause elide\n+   the GOMP_alloc/GOMP_free transformations.  */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc\" 40 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_free\" 40 \"gimple\" } } */\n+\n+/* FIXME: This almost certainly needs tweaks for systems where alignof(int)\n+   is not 4.  */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(32,\" 24 \"gimple\" } } */\n+\n+/* The align argument is at least alignof(int) (4) in all cases, thus we can subtract\n+   the cases where align >= 8 from the total cases to determine how many cases\n+   there are where align == 4.\n+   40 - 24 = 16.\n+   Please update this math if changes are made to this test so everything\n+   is (somewhat) coherent.  */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(4,\" 16 \"gimple\" } } */\n+\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 0B\\\\\\)\" 16 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 1\\\\\\)\" 8 \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\\\(\\\\d+, \\\\d+, 2\\\\\\)\" 16 \"gimple\" } } */\n+\n+struct S0 {\n+  int _v;\n+  S0(int v) : _v(v) {}\n+  operator int() const { return 42; }\n+};\n+\n+struct S1 {\n+  int _v[2];\n+  S1(int v) : _v{v, v} {}\n+  operator int() const { return 42; }\n+};\n+\n+/*****************************\n+ * template parameter (auto) *\n+ *****************************/\n+\n+int auto_parm(auto)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a)\n+  static_assert(alignof(a) == alignof(int));\n+  return a;\n+}\n+\n+int auto_parm_align(auto)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32)\n+  static_assert(alignof(a) == alignof(int));\n+  return a;\n+}\n+\n+int auto_parm_alloc_0(auto)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) allocator(omp_default_mem_alloc)\n+  static_assert(alignof(a) == alignof(int));\n+  return a;\n+}\n+\n+int auto_parm_alloc_1(auto)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)\n+  static_assert(alignof(a) == alignof(int));\n+  return a;\n+}\n+\n+int auto_parm_align_alloc_0(auto)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)\n+  static_assert(alignof(a) == alignof(int));\n+  return a;\n+}\n+\n+int auto_parm_align_alloc_1(auto)\n+{\n+  int a = 42;\n+  #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)\n+  static_assert(alignof(a) == alignof(int));\n+  return a;\n+}\n+\n+/* 5 cases per expansion, auto_parm_alloc_0 uses omp_default_mem_alloc\n+   without an align clause.\n+   2 allocator==0B,\n+   1 allocator==1 (because 1 case elided),\n+   2 allocator==2 cases per expansion.\n+\n+   3 align==32 cases per expansion.  */\n+#define INSTANTIATE_ALL_WITH_T(type)\t\t\\\n+  do {\t\t\t\t\t\t\\\n+    type a = 42;\t\t\t\t\\\n+    int v0 = auto_parm(a);\t\t\t\\\n+    int v1 = auto_parm_align(a);\t\t\\\n+    int v2 = auto_parm_alloc_0(a);\t\t\\\n+    int v3 = auto_parm_alloc_1(a);\t\t\\\n+    int v4 = auto_parm_align_alloc_0(a);\t\\\n+    int v5 = auto_parm_align_alloc_1(a);\t\\\n+    static_cast<void>(v0);\t\t\t\\\n+    static_cast<void>(v1);\t\t\t\\\n+    static_cast<void>(v2);\t\t\t\\\n+    static_cast<void>(v3);\t\t\t\\\n+    static_cast<void>(v4);\t\t\t\\\n+    static_cast<void>(v5);\t\t\t\\\n+  } while (false)\n+\n+/* 4 * 5 cases, 20.\n+   4 * 2 allocator==0B cases, 8.\n+   4 * 1 allocator==1 cases, 4.\n+   4 * 2 allocator==2 cases, 8.\n+   4 * 3 align==32 cases, 12.  */\n+void instantiate_auto_parm_tests()\n+{\n+  INSTANTIATE_ALL_WITH_T(int);\n+  INSTANTIATE_ALL_WITH_T(float);\n+  INSTANTIATE_ALL_WITH_T(S0);\n+  INSTANTIATE_ALL_WITH_T(S1);\n+}\n+\n+#undef INSTANTIATE_ALL_WITH_T\n+\n+\n+auto dep_local_auto_parm(auto p)\n+{\n+  auto a = p;\n+  #pragma omp allocate(a)\n+  static_assert(alignof(a) == alignof(decltype(p)));\n+  return a;\n+}\n+\n+auto dep_local_auto_parm_align(auto p)\n+{\n+  auto a = p;\n+  #pragma omp allocate(a) align(32)\n+  static_assert(alignof(a) == alignof(decltype(p)));\n+  return a;\n+}\n+\n+auto dep_local_auto_parm_alloc_0(auto p)\n+{\n+  auto a = p;\n+  #pragma omp allocate(a) allocator(omp_default_mem_alloc)\n+  static_assert(alignof(a) == alignof(decltype(p)));\n+  return a;\n+}\n+\n+auto dep_local_auto_parm_alloc_1(auto p)\n+{\n+  auto a = p;\n+  #pragma omp allocate(a) allocator(omp_large_cap_mem_alloc)\n+  static_assert(alignof(a) == alignof(decltype(p)));\n+  return a;\n+}\n+\n+auto dep_local_auto_parm_align_alloc_0(auto p)\n+{\n+  auto a = p;\n+  #pragma omp allocate(a) align(32) allocator(omp_default_mem_alloc)\n+  static_assert(alignof(a) == alignof(decltype(p)));\n+  return a;\n+}\n+\n+auto dep_local_auto_parm_align_alloc_1(auto p)\n+{\n+  auto a = p;\n+  #pragma omp allocate(a) align(32) allocator(omp_large_cap_mem_alloc)\n+  static_assert(alignof(a) == alignof(decltype(p)));\n+  return a;\n+}\n+\n+/* 5 cases per expansion, dep_local_auto_parm_alloc_0 uses omp_default_mem_alloc\n+   without an align clause.\n+   2 allocator==0B,\n+   1 allocator==1 (because 1 case elided),\n+   2 allocator==2 cases per expansion.\n+\n+   3 align==32 cases per expansion.  */\n+#define INSTANTIATE_ALL_WITH_T(type)\t\t\t\\\n+  do {\t\t\t\t\t\t\t\\\n+    type a = 42;\t\t\t\t\t\\\n+    type v0 = dep_local_auto_parm(a);\t\t\t\\\n+    type v1 = dep_local_auto_parm_align(a);\t\t\\\n+    type v2 = dep_local_auto_parm_alloc_0(a);\t\t\\\n+    type v3 = dep_local_auto_parm_alloc_1(a);\t\t\\\n+    type v4 = dep_local_auto_parm_align_alloc_0(a);\t\\\n+    type v5 = dep_local_auto_parm_align_alloc_1(a);\t\\\n+    static_cast<void>(v0);\t\t\t\t\\\n+    static_cast<void>(v1);\t\t\t\t\\\n+    static_cast<void>(v2);\t\t\t\t\\\n+    static_cast<void>(v3);\t\t\t\t\\\n+    static_cast<void>(v4);\t\t\t\t\\\n+    static_cast<void>(v5);\t\t\t\t\\\n+  } while (false)\n+\n+/* 4 * 5 cases, 20.\n+   4 * 2 allocator==0B cases, 8.\n+   4 * 1 allocator==1 cases, 4.\n+   4 * 2 allocator==2 cases, 8.\n+   4 * 3 align==32 cases, 12.  */\n+void instantiate_dep_local_auto_parm_tests()\n+{\n+  INSTANTIATE_ALL_WITH_T(int);\n+  INSTANTIATE_ALL_WITH_T(float);\n+  INSTANTIATE_ALL_WITH_T(S0);\n+  INSTANTIATE_ALL_WITH_T(S1);\n+}\n+\n+#undef INSTANTIATE_ALL_WITH_T\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-8.C b/gcc/testsuite/g++.dg/gomp/allocate-8.C\nnew file mode 100644\nindex 00000000000..2cfdc632cf0\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-8.C\n@@ -0,0 +1,48 @@\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+#include \"allocate-allocator-handle.h\"\n+\n+/* Check simple cases that do not have an align clause and the allocator clause\n+   folds to omp_default_mem_alloc.  These cases are supposed to elide\n+   the usual transformations (calling __builtin_GOMP_alloc/__builtin_GOMP_free)\n+   that are usually applied to vars in an allocate directive.\n+\n+   It is not entirely clear if this is correct behavior, and due to differences\n+   in how C++ works we can't support this for all cases, such as for exprs with\n+   calls to constexpr functions.\n+   At the very least we should make sure the obvious cases still work as\n+   expected.  */\n+\n+/* { dg-final { scan-tree-dump-not \"__builtin_GOMP_alloc\" \"gimple\" } } */\n+/* { dg-final { scan-tree-dump-not \"__builtin_GOMP_free\" \"gimple\" } } */\n+\n+int hardcoded_alloc(int p)\n+{\n+  int a = p;\n+  #pragma omp allocate(a) allocator(omp_default_mem_alloc)\n+  return a;\n+}\n+\n+int converted_expr_alloc(int p)\n+{\n+  int a = p;\n+  #pragma omp allocate(a) allocator(omp_allocator_handle_t(1))\n+  return a;\n+}\n+\n+int simple_expr_alloc(int p)\n+{\n+  int a = p;\n+  #pragma omp allocate(a) allocator(omp_allocator_handle_t(0+1))\n+  return a;\n+}\n+\n+template<omp_allocator_handle_t Alloc>\n+int nttp_alloc(int p)\n+{\n+  int a = p;\n+  #pragma omp allocate(a) allocator(Alloc)\n+  return a;\n+}\n+template int nttp_alloc<omp_default_mem_alloc>(int);\n+\n+\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-9.C b/gcc/testsuite/g++.dg/gomp/allocate-9.C\nnew file mode 100644\nindex 00000000000..066b8c617a7\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-9.C\n@@ -0,0 +1,78 @@\n+/* { dg-do compile { target c++23 } } */\n+/* { dg-additional-options \"-fdump-tree-gimple\" } */\n+\n+#include \"allocate-allocator-handle.h\"\n+\n+/* Test allocator clause is conditionally manifestly constant evaluated.\n+\n+   This test needs some work to actually detect if it is doing the right thing\n+   in the static cases, but we barely implement allocation for those cases so\n+   there isn't really much to detect.\n+   \n+   For the non-static cases, it would be ideal if we checked whether it\n+   optimizes properly, currently that is not being done.  */\n+\n+\n+/* Note, in practice this isn't actually useful as user handles are (AFAIK)\n+   assigned by the runtime, this is just for testing.  */\n+constexpr omp_allocator_handle_t get_alloc_fn_foldable ()\n+{\n+  if consteval {\n+    return omp_large_cap_mem_alloc;\n+  }\n+  else {\n+    return omp_allocator_handle_t{128};\n+  }\n+}\n+\n+omp_allocator_handle_t my_global_user_handle = omp_allocator_handle_t{256};\n+\n+constexpr omp_allocator_handle_t get_alloc_fn_non_foldable ()\n+{\n+  if consteval {\n+    return omp_large_cap_mem_alloc;\n+  }\n+  else {\n+    return my_global_user_handle;\n+  }\n+}\n+\n+\n+/* This could optimize to 128 but is not required to.  */\n+int non_manifest_cx_foldable (int p)\n+{\n+  int non_static_a = p;\n+  #pragma omp allocate(non_static_a) allocator(get_alloc_fn_foldable ())\n+  return non_static_a;\n+}\n+/* { dg-final { scan-tree-dump-times \"= get_alloc_fn_foldable\" 1 \"gimple\" } } */\n+\n+/* At best this can optimize to 'my_global_user_handle'.  */\n+int non_manifest_cx_non_foldable (int p)\n+{\n+  int non_static_b = p;\n+  #pragma omp allocate(non_static_b) allocator(get_alloc_fn_non_foldable ())\n+  return non_static_b;\n+}\n+/* { dg-final { scan-tree-dump-times \"= get_alloc_fn_non_foldable\" 1 \"gimple\" } } */\n+\n+/* In both of these cases, the allocator clause's expr is\n+   manifestly constant evaluated.  */\n+\n+/* This MUST fold to omp_large_cap_mem_alloc.  */\n+int manifest_cx_foldable (int p)\n+{\n+  static int static_a = p;\n+  #pragma omp allocate(static_a) allocator(get_alloc_fn_foldable ())\n+  return static_a;\n+}\n+\n+/* This MUST fold to omp_large_cap_mem_alloc, calling get_alloc_fn_non_foldable\n+   is intentional, the function is only non-foldable if the allocator clause\n+   is not manifestly constant evaluated.  */\n+int manifest_cx_non_foldable (int p)\n+{\n+  static int static_b = p;\n+  #pragma omp allocate(static_b) allocator(get_alloc_fn_non_foldable ())\n+  return static_b;\n+}\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h b/gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h\nnew file mode 100644\nindex 00000000000..c629df158c1\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-allocator-handle.h\n@@ -0,0 +1,18 @@\n+typedef __UINTPTR_TYPE__ omp_uintptr_t;\n+\n+typedef enum omp_allocator_handle_t\n+#if __cplusplus >= 201103L\n+: omp_uintptr_t\n+#endif\n+{\n+  omp_null_allocator = 0,\n+  omp_default_mem_alloc = 1,\n+  omp_large_cap_mem_alloc = 2,\n+  omp_const_mem_alloc = 3,\n+  omp_high_bw_mem_alloc = 4,\n+  omp_low_lat_mem_alloc = 5,\n+  omp_cgroup_mem_alloc = 6,\n+  omp_pteam_mem_alloc = 7,\n+  omp_thread_mem_alloc = 8,\n+  __omp_allocator_handle_t_max__ = __UINTPTR_MAX__\n+} omp_allocator_handle_t;\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-handles-1.C b/gcc/testsuite/g++.dg/gomp/allocate-handles-1.C\nnew file mode 100644\nindex 00000000000..c0c59a30a3c\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-handles-1.C\n@@ -0,0 +1,63 @@\n+#include \"allocate-allocator-handle.h\"\n+\n+/* If the following fails because of added predefined allocators, please update\n+   - include/gomp-constants.h's GOMP_OMP_PREDEF_ALLOC_MAX or GOMP_OMPX_PREDEF_ALLOC_MAX\n+   - libgomp/env.c's parse_allocator\n+   - libgomp/libgomp.texi (document the new values - multiple locations)\n+   - gcc/testsuite/c-c++-common/gomp/allocate-9.c (fix the hardcoded values)\n+   - gcc/testsuite/g++.dg/gomp/allocate-handles-2.C (update GOMP_OMP_PREDEF_ALLOC_MAX and/or GOMP_OMPX_PREDEF_ALLOC_MAX)\n+   + ensure that the memory-spaces are also up to date. */\n+\n+/* I had wanted to simply include /include/gomp-constants.h to ensure\n+   synchronization, while also having hardcoded values as a canary, but\n+   including files from that directory does not seem to be supported.  */\n+#define GOMP_OMP_PREDEF_ALLOC_MAX\t8\n+#define GOMP_OMPX_PREDEF_ALLOC_MIN\t200\n+#define GOMP_OMPX_PREDEF_ALLOC_MAX\t201\n+\n+int g0 = 42; /* { dg-note \"'g0' declared here\" \"\" { xfail *-*-* } } */\n+#pragma omp allocate(g0) allocator(omp_null_allocator)\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target *-*-* } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+/* { dg-note \"expression evaluates to '0'\" \"\" { target *-*-* } .-3 } */\n+int g1 = 42; /* { dg-note \"'g1' declared here\" \"\" { xfail *-*-* } }*/\n+#pragma omp allocate(g1) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMP_PREDEF_ALLOC_MAX + 1)) \n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information\" { target *-*-* } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+/* { dg-note \"expression evaluates to '9'\" \"\" { target *-*-* } .-3 } */\n+int g2 = 42; /* { dg-note \"'g2' declared here\" \"\" { xfail *-*-* } }*/\n+#pragma omp allocate(g2) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MIN - 1))\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target *-*-* } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+/* { dg-note \"expression evaluates to '199'\" \"\" { target *-*-* } .-3 } */\n+int g3 = 42; /* { dg-note \"'g3' declared here\" \"\" { xfail *-*-* } }*/\n+#pragma omp allocate(g3) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MAX + 1))\n+/* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information\" { target *-*-* } .-1 } */\n+/* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+/* { dg-note \"expression evaluates to '202'\" \"\" { target *-*-* } .-3 } */\n+\n+void test_predefined_allocs()\n+{\n+  static int a0 = 42; /* { dg-note \"'a0' declared here\" \"\" { xfail *-*-* } }*/\n+  #pragma omp allocate(a0) allocator(omp_null_allocator)\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target *-*-* } .-1 } */\n+  /* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+  /* { dg-note \"expression evaluates to '0'\" \"\" { target *-*-* } .-3 } */\n+  static int a1 = 42; /* { dg-note \"'a1' declared here\" \"\" { xfail *-*-* } }*/\n+  #pragma omp allocate(a1) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMP_PREDEF_ALLOC_MAX + 1))\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information\" { target *-*-* } .-1 } */\n+  /* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+  /* { dg-note \"expression evaluates to '9'\" \"\" { target *-*-* } .-3 } */\n+  static int a2 = 42; /* { dg-note \"'a2' declared here\" \"\" { xfail *-*-* } }*/\n+  #pragma omp allocate(a2) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MIN - 1))\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"\" { target *-*-* } .-1 } */\n+  /* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+  /* { dg-note \"expression evaluates to '199'\" \"\" { target *-*-* } .-3 } */\n+  static int a3 = 42; /* { dg-note \"'a3' declared here\" \"\" { xfail *-*-* } }*/\n+  #pragma omp allocate(a3) allocator(static_cast<omp_allocator_handle_t>(GOMP_OMPX_PREDEF_ALLOC_MAX + 1))\n+  /* { dg-error \"'allocator' clause requires a constant predefined allocator\" \"If this test fails because of added predefined allocators please ensure everything is updated accordingly, see this test case for more information\" { target *-*-* } .-1 } */\n+  /* { dg-note \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail *-*-* } .-2 } */\n+  /* { dg-note \"expression evaluates to '202'\" \"\" { target *-*-* } .-3 } */\n+}\n+\n+/* { dg-bogus \"because one or more variables with static storage duration appear in the 'allocate' directive\" \"\" { xfail c++ } 0 }*/\ndiff --git a/gcc/testsuite/g++.dg/gomp/allocate-handles-2.C b/gcc/testsuite/g++.dg/gomp/allocate-handles-2.C\nnew file mode 100644\nindex 00000000000..89949ee0fef\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/gomp/allocate-handles-2.C\n@@ -0,0 +1,45 @@\n+/* { dg-do compile { target c++11 } } */\n+#include \"allocate-allocator-handle.h\"\n+\n+/* I had wanted to simply include /include/gomp-constants.h to ensure\n+   synchronization, but including files from that directory does not seem\n+   to be supported.  */\n+#define GOMP_OMP_PREDEF_ALLOC_MAX\t8\n+#define GOMP_OMPX_PREDEF_ALLOC_MIN\t200\n+#define GOMP_OMPX_PREDEF_ALLOC_MAX\t201\n+\n+/* Test that all predefined allocators are correctly treated as predefined.  */\n+\n+template<omp_allocator_handle_t Alloc>\n+void test_predefined_alloc()\n+{\n+  static int a = 42;\n+  #pragma omp allocate(a) allocator(Alloc)\n+}\n+\n+/* Because this is written to work as far back as c++11 it is a little bit\n+   crusty.  It is metaprogrammed to automatically test the full ranges\n+   specified above.  */\n+\n+template<omp_allocator_handle_t...>\n+struct sequence {};\n+\n+template<__UINTPTR_TYPE__ Offset, __UINTPTR_TYPE__... Is>\n+using modified = sequence<static_cast<omp_allocator_handle_t>(Is + Offset)...>;\n+\n+template<__UINTPTR_TYPE__ Start, __UINTPTR_TYPE__ End>\n+using make_offset_sequence = modified<Start, __integer_pack(End - Start)...>;\n+\n+template<omp_allocator_handle_t... Allocs>\n+void unpack(sequence<Allocs...>)\n+{\n+  int helper[] = {(test_predefined_alloc<Allocs>(), 0)...};\n+}\n+\n+void do_tests()\n+{\n+  /* make_sequence creates a sequence [Start, End) while the *_MAX values are\n+     inclusive, add 1 to the End arg to create an exclusive range.  */\n+  unpack(make_offset_sequence<1, GOMP_OMP_PREDEF_ALLOC_MAX + 1>{});\n+  unpack(make_offset_sequence<GOMP_OMPX_PREDEF_ALLOC_MIN, GOMP_OMPX_PREDEF_ALLOC_MAX + 1>{});\n+}\ndiff --git a/libgomp/testsuite/libgomp.c++/allocate-2.C b/libgomp/testsuite/libgomp.c++/allocate-2.C\nnew file mode 100644\nindex 00000000000..f79cada1777\n--- /dev/null\n+++ b/libgomp/testsuite/libgomp.c++/allocate-2.C\n@@ -0,0 +1,329 @@\n+/* { dg-do run } */\n+/* { dg-additional-options \"-fdump-tree-omplower\" } */\n+\n+/* For the 4 vars in omp_parallel, 4 in omp_target and 1 of 2 in each of no_alloc{,2}_func.  */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_alloc \\\\(\" 10 \"omplower\" } } */\n+/* { dg-final { scan-tree-dump-times \"__builtin_GOMP_free \\\\(\" 10 \"omplower\" } } */\n+\n+#include <omp.h>\n+\n+\n+void\n+check_int (int *x, int y)\n+{\n+  if (*x != y)\n+    __builtin_abort ();\n+}\n+\n+void\n+check_ptr (int **x, int *y)\n+{\n+  if (*x != y)\n+    __builtin_abort ();\n+}\n+\n+\n+template<typename t>\n+t\n+no_alloc_func ()\n+{\n+  /* There is no __builtin_GOMP_alloc / __builtin_GOMP_free as\n+     allocator == omp_default_mem_alloc (known at compile time. */\n+  t no_alloc, alloc_has_align = 3;\n+  #pragma omp allocate(no_alloc) allocator(omp_default_mem_alloc)\n+  /* But this one is allocated because of align. */\n+  #pragma omp allocate(alloc_has_align) allocator(omp_default_mem_alloc) align(sizeof(t))\n+  no_alloc = 7;\n+  return no_alloc + alloc_has_align;\n+}\n+\n+template<typename t>\n+t\n+no_alloc2_func()\n+{\n+  /* There is no __builtin_GOMP_alloc / __builtin_GOMP_free as\n+     no_alloc2 is TREE_UNUSED.  But there is for is_alloc2.  */\n+  t no_alloc2, is_alloc2;\n+  #pragma omp allocate(no_alloc2, is_alloc2)\n+  is_alloc2 = 7;\n+  return is_alloc2;\n+}\n+\n+\n+template<typename t>\n+void\n+omp_parallel ()\n+{\n+  int n = 6;\n+  t iii = 5, jjj[5], kkk[n];\n+  t *ptr = (t *) 0x1234;\n+  #pragma omp allocate(iii, jjj, kkk, ptr)\n+\n+  for (int i = 0; i < 5; i++)\n+    jjj[i] = 3*i;\n+  for (int i = 0; i < 6; i++)\n+    kkk[i] = 7*i;\n+\n+  #pragma omp parallel default(none) firstprivate(iii, jjj, kkk, ptr) if(0)\n+  {\n+    if (iii != 5)\n+      __builtin_abort();\n+    iii = 7;\n+    check_int (&iii, 7);\n+    for (int i = 0; i < 5; i++)\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 6; i++)\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 5; i++)\n+      jjj[i] = 4*i;\n+    for (int i = 0; i < 6; i++)\n+      kkk[i] = 8*i;\n+    for (int i = 0; i < 5; i++)\n+      check_int (&jjj[i], 4*i);\n+    for (int i = 0; i < 6; i++)\n+      check_int (&kkk[i], 8*i);\n+    if (ptr != (int *) 0x1234)\n+      __builtin_abort ();\n+    ptr = (int *) 0xabcd;\n+    if (ptr != (int *) 0xabcd)\n+      __builtin_abort ();\n+    check_ptr (&ptr, (int *) 0xabcd);\n+  }\n+  if (iii != 5)\n+    __builtin_abort ();\n+  check_int (&iii, 5);\n+  for (int i = 0; i < 5; i++)\n+    {\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+      check_int (&jjj[i], 3*i);\n+    }\n+  for (int i = 0; i < 6; i++)\n+    {\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+      check_int (&kkk[i], 7*i);\n+    }\n+  if (ptr != (int *) 0x1234)\n+    __builtin_abort ();\n+  check_ptr (&ptr, (int *) 0x1234);\n+\n+  #pragma omp parallel default(firstprivate) if(0)\n+  {\n+    if (iii != 5)\n+      __builtin_abort();\n+    iii = 7;\n+    check_int (&iii, 7);\n+    for (int i = 0; i < 5; i++)\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 6; i++)\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 5; i++)\n+      jjj[i] = 4*i;\n+    for (int i = 0; i < 6; i++)\n+      kkk[i] = 8*i;\n+    for (int i = 0; i < 5; i++)\n+      check_int (&jjj[i], 4*i);\n+    for (int i = 0; i < 6; i++)\n+      check_int (&kkk[i], 8*i);\n+    if (ptr != (int *) 0x1234)\n+      __builtin_abort ();\n+    ptr = (int *) 0xabcd;\n+    if (ptr != (int *) 0xabcd)\n+      __builtin_abort ();\n+    check_ptr (&ptr, (int *) 0xabcd);\n+  }\n+  if (iii != 5)\n+    __builtin_abort ();\n+  check_int (&iii, 5);\n+  for (int i = 0; i < 5; i++)\n+    {\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+      check_int (&jjj[i], 3*i);\n+    }\n+  for (int i = 0; i < 6; i++)\n+    {\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+      check_int (&kkk[i], 7*i);\n+    }\n+  if (ptr != (int *) 0x1234)\n+    __builtin_abort ();\n+  check_ptr (&ptr, (int *) 0x1234);\n+}\n+\n+\n+template<typename t>\n+void\n+omp_target ()\n+{\n+  int n = 6;\n+  t iii = 5, jjj[5], kkk[n];\n+  t *ptr = (int *) 0x1234;\n+  #pragma omp allocate(iii, jjj, kkk, ptr)\n+\n+  for (int i = 0; i < 5; i++)\n+    jjj[i] = 3*i;\n+  for (int i = 0; i < 6; i++)\n+    kkk[i] = 7*i;\n+\n+  #pragma omp target defaultmap(none) firstprivate(iii, jjj, kkk, ptr)\n+  {\n+    if (iii != 5)\n+      __builtin_abort();\n+    iii = 7;\n+    check_int (&iii, 7);\n+    for (int i = 0; i < 5; i++)\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 6; i++)\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 5; i++)\n+      jjj[i] = 4*i;\n+    for (int i = 0; i < 6; i++)\n+      kkk[i] = 8*i;\n+    for (int i = 0; i < 5; i++)\n+      check_int (&jjj[i], 4*i);\n+    for (int i = 0; i < 6; i++)\n+      check_int (&kkk[i], 8*i);\n+    if (ptr != (int *) 0x1234)\n+      __builtin_abort ();\n+    ptr = (int *) 0xabcd;\n+    if (ptr != (int *) 0xabcd)\n+      __builtin_abort ();\n+    check_ptr (&ptr, (int *) 0xabcd);\n+  }\n+  if (iii != 5)\n+    __builtin_abort ();\n+  check_int (&iii, 5);\n+  for (int i = 0; i < 5; i++)\n+    {\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+      check_int (&jjj[i], 3*i);\n+    }\n+  for (int i = 0; i < 6; i++)\n+    {\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+      check_int (&kkk[i], 7*i);\n+    }\n+  if (ptr != (int *) 0x1234)\n+    __builtin_abort ();\n+  check_ptr (&ptr, (int *) 0x1234);\n+\n+  #pragma omp target defaultmap(firstprivate)\n+  {\n+    if (iii != 5)\n+      __builtin_abort();\n+    iii = 7;\n+    check_int (&iii, 7);\n+    for (int i = 0; i < 5; i++)\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 6; i++)\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 5; i++)\n+      jjj[i] = 4*i;\n+    for (int i = 0; i < 6; i++)\n+      kkk[i] = 8*i;\n+    for (int i = 0; i < 5; i++)\n+      check_int (&jjj[i], 4*i);\n+    for (int i = 0; i < 6; i++)\n+      check_int (&kkk[i], 8*i);\n+    if (ptr != (int *) 0x1234)\n+      __builtin_abort ();\n+    ptr = (int *) 0xabcd;\n+    if (ptr != (int *) 0xabcd)\n+      __builtin_abort ();\n+    check_ptr (&ptr, (int *) 0xabcd);\n+  }\n+  if (iii != 5)\n+    __builtin_abort ();\n+  check_int (&iii, 5);\n+  for (int i = 0; i < 5; i++)\n+    {\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+      check_int (&jjj[i], 3*i);\n+    }\n+  for (int i = 0; i < 6; i++)\n+    {\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+      check_int (&kkk[i], 7*i);\n+    }\n+  if (ptr != (int *) 0x1234)\n+    __builtin_abort ();\n+  check_ptr (&ptr, (int *) 0x1234);\n+\n+  #pragma omp target defaultmap(tofrom)\n+  {\n+    if (iii != 5)\n+      __builtin_abort();\n+    iii = 7;\n+    check_int (&iii, 7);\n+    for (int i = 0; i < 5; i++)\n+      if (jjj[i] != 3*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 6; i++)\n+      if (kkk[i] != 7*i)\n+\t__builtin_abort ();\n+    for (int i = 0; i < 5; i++)\n+      jjj[i] = 4*i;\n+    for (int i = 0; i < 6; i++)\n+      kkk[i] = 8*i;\n+    for (int i = 0; i < 5; i++)\n+      check_int (&jjj[i], 4*i);\n+    for (int i = 0; i < 6; i++)\n+      check_int (&kkk[i], 8*i);\n+    if (ptr != (int *) 0x1234)\n+      __builtin_abort ();\n+    ptr = (int *) 0xabcd;\n+    if (ptr != (int *) 0xabcd)\n+      __builtin_abort ();\n+    check_ptr (&ptr, (int *) 0xabcd);\n+  }\n+\n+  if (iii != 7)\n+    __builtin_abort ();\n+  check_int (&iii, 7);\n+  for (int i = 0; i < 5; i++)\n+    {\n+      if (jjj[i] != 4*i)\n+\t__builtin_abort ();\n+      check_int (&jjj[i], 4*i);\n+    }\n+  for (int i = 0; i < 6; i++)\n+    {\n+      if (kkk[i] != 8*i)\n+\t__builtin_abort ();\n+      check_int (&kkk[i], 8*i);\n+    }\n+  if (ptr != (int *) 0xabcd)\n+    __builtin_abort ();\n+  check_ptr (&ptr, (int *) 0xabcd);\n+}\n+\n+int\n+foo()\n+{\n+  return no_alloc_func<int>() + no_alloc2_func<int>();\n+}\n+\n+int\n+main ()\n+{\n+  omp_parallel<int> ();\n+  omp_target<int> ();\n+  if (foo() != 10 + 7)\n+    __builtin_abort ();\n+  return 0;\n+}\ndiff --git a/libgomp/testsuite/libgomp.c/allocate-4.c b/libgomp/testsuite/libgomp.c-c++-common/allocate-4.c\nsimilarity index 95%\nrename from libgomp/testsuite/libgomp.c/allocate-4.c\nrename to libgomp/testsuite/libgomp.c-c++-common/allocate-4.c\nindex e81cc4093aa..706c8510b84 100644\n--- a/libgomp/testsuite/libgomp.c/allocate-4.c\n+++ b/libgomp/testsuite/libgomp.c-c++-common/allocate-4.c\n@@ -1,6 +1,3 @@\n-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */\n-/* NOTE: { target c } is unsupported with with the C compiler.  */\n-\n /* { dg-do run } */\n /* { dg-additional-options \"-fdump-tree-gimple\" } */\n \ndiff --git a/libgomp/testsuite/libgomp.c/allocate-5.c b/libgomp/testsuite/libgomp.c-c++-common/allocate-5.c\nsimilarity index 96%\nrename from libgomp/testsuite/libgomp.c/allocate-5.c\nrename to libgomp/testsuite/libgomp.c-c++-common/allocate-5.c\nindex beaf16440e1..3bbe78db840 100644\n--- a/libgomp/testsuite/libgomp.c/allocate-5.c\n+++ b/libgomp/testsuite/libgomp.c-c++-common/allocate-5.c\n@@ -1,6 +1,3 @@\n-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */\n-/* NOTE: { target c } is unsupported with with the C compiler.  */\n-\n /* { dg-do run } */\n /* { dg-additional-options \"-fdump-tree-gimple\" } */\n \ndiff --git a/libgomp/testsuite/libgomp.c/allocate-6.c b/libgomp/testsuite/libgomp.c-c++-common/allocate-6.c\nsimilarity index 98%\nrename from libgomp/testsuite/libgomp.c/allocate-6.c\nrename to libgomp/testsuite/libgomp.c-c++-common/allocate-6.c\nindex 6d7278ce571..669581b327d 100644\n--- a/libgomp/testsuite/libgomp.c/allocate-6.c\n+++ b/libgomp/testsuite/libgomp.c-c++-common/allocate-6.c\n@@ -1,6 +1,3 @@\n-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */\n-/* NOTE: { target c } is unsupported with with the C compiler.  */\n-\n /* { dg-do run } */\n /* { dg-additional-options \"-fdump-tree-omplower\" } */\n \ndiff --git a/libgomp/testsuite/libgomp.c/allocate-7.c b/libgomp/testsuite/libgomp.c-c++-common/allocate-7.c\nsimilarity index 90%\nrename from libgomp/testsuite/libgomp.c/allocate-7.c\nrename to libgomp/testsuite/libgomp.c-c++-common/allocate-7.c\nindex 1fa92d889d0..9968eb19005 100644\n--- a/libgomp/testsuite/libgomp.c/allocate-7.c\n+++ b/libgomp/testsuite/libgomp.c-c++-common/allocate-7.c\n@@ -1,6 +1,3 @@\n-/* TODO: move to ../libgomp.c-c++-common once C++ is implemented. */\n-/* NOTE: { target c } is unsupported with with the C compiler.  */\n-\n /* { dg-do run } */\n \n #include <omp.h>\n","prefixes":["04/12"]}