diff mbox series

c++: Allow overloaded builtins to be used in SFINAE context

Message ID 20241010091217.1150849-1-mmalcomson@nvidia.com
State New
Headers show
Series c++: Allow overloaded builtins to be used in SFINAE context | expand

Commit Message

Matthew Malcomson Oct. 10, 2024, 9:12 a.m. UTC
From: Matthew Malcomson <mmalcomson@nvidia.com>

This commit newly introduces the ability to use overloaded builtins in
C++ SFINAE context.

The goal behind this is in order to ensure there is a single mechanism
that libstdc++ can use to determine whether a given type can be used in
the atomic fetch_add (and similar) builtins.  I am working on another
patch that hopes to use this mechanism to identify whether fetch_add
(and similar) work on floating point types.

Current state of the world:

    GCC currently exposes resolved versions of these builtins to the
    user, so for GCC it's currently possible to use tests similar to the
    below to check for atomic loads on a 2 byte sized object.
      #if __has_builtin(__atomic_load_2)
    Clang does not expose resolved versions of the atomic builtins.

    clang currently allows SFINAE on builtins, so that C++ code can
    check whether a builtin is available on a given type.
    GCC does not (and that is what this patch aims to change).

    Libraries like libatomic can check whether a given atomic builtin
    can work on a given type by using autoconf to check for a
    miscompilation when attempting such a use.

My goal:
    I would like to enable floating point fetch_add (and similar) in
    GCC, in order to use those overloads in libstdc++ implementation of
    atomic<float>::fetch_add.
    This should allow compilers targeting GPU's which have floating
    point fetch_add instructions to emit optimal code.

    In order to do that I need some consistent mechanism that libstdc++
    can use to identify whether the fetch_add builtins have floating
    point overloads (and for which types these exist).

    I would hence like to enable SFINAE on builtins, so that libstdc++
    can use that mechanism for the floating point fetch_add builtins.
    Discussion with J Wakely about which approach to take for libstdc++:
    https://gcc.gnu.org/pipermail/gcc-patches/2024-October/664363.html

Implementation follows the existing mechanism for handling SFINAE
contexts in c-common.cc.  A boolean is passed into the c-common.cc
function indicating whether these functions should emit errors or not.
This boolean comes from `complain & tf_error` in the C++ frontend.
(Similar to other functions like valid_array_size_p and
c_build_vec_perm_expr).

This is done both for resolve_overloaded_builtin and
check_builtin_function_arguments, both of which can be used in SFINAE
contexts.
    N.b. I attempted to trigger something using the `reject_gcc_builtin`
    function in an SFINAE context.  Given the context where this
    function is called from the C++ frontend it looks like it may be
    possible, but I did not manage to trigger this in template context
    by attempting to do something similar to the testcases added around
    those calls.
    - I would appreciate any feedback on whether this is something that
      can happen in a template context, and if so some help writing a
      relevant testcase for it.

Both of these functions have target hooks for target specific builtins
that I have updated to take the extra boolean flag.  I have not adjusted
the functions implementing those target hooks (except to update the
declarations) so target specific builtins will still error in SFINAE
contexts.
- N.b. I could imagine not updating the target hook definition since
  nothing would use that change.  However I figure that allowing targets
  to decide this behaviour would be the right thing to do eventually,
  and since this is the target-independent part of the change to do that
  this patch should make that change.
  Could adjust if others disagree.

Other relevant points that I'd appreciate reviewers check:
- I did not pass this new flag through
  atomic_bitint_fetch_using_cas_loop since the _BitInt type is not
  available in the C++ frontend and I didn't want if conditions that can
  not be executed in the source.
- I only test non-compile-time-constant types with SVE types, since I do
  not know of a way to get a VLA into a SFINAE context.
- While writing tests I noticed a few differences with clang in this
  area.  I don't think they are problematic but am mentioning them for
  completeness and to allow others to judge if these are a problem).
  - atomic_fetch_add on a boolean is allowed by clang.
  - With SFINAE GCC is happy with multiple definitions of a differently
    typed template as long as they aren't instantiated, while clang is
    not.  This seems to be a general difference around clang and GCC and
    not specific to builtins.
    I.e. two template definitions with the same name where one
    specialises on `myfunc (std::declval<T>())` and another on
    `myfunc (std::declval<T>(), std::declval<T>())` will not give an
    error in GCC unless one attempts to instantiate the template.
    However it will give an error in clang on the template redefinition.
- I do not block the warning about using
  __builtin_speculation_safe_value on a target that does not have any
  active mitigation defined.  I do this since when this happens we do
  not return error_mark_node, which means that if a user attempted to
  use SFINAE to check for this situation that would silently fail if the
  warning were suppressed.
  N.b. this is also a warning rather than an error.
  Similarly I do not block the warning about an invalid memory model
  argument in get_atomic_generic_size.

Bootstrap and regression tested on AArch64 and x86_64.
Built first stage on targets whose target hook declaration needed
updated (though did not regtest etc).  Targets triplets I built in order
to check the backend specific changes I made:
   - arm-none-linux-gnueabihf
   - avr-linux-gnu
   - riscv-linux-gnu
   - powerpc-linux-gnu
   - s390x-linux-gnu

Ok for commit to trunk?

gcc/c-family/ChangeLog:

	* c-common.cc (builtin_function_validate_nargs,
	check_builtin_function_arguments,
	speculation_safe_value_resolve_call,
	speculation_safe_value_resolve_params, sync_resolve_size,
	sync_resolve_params, get_atomic_generic_size,
	resolve_overloaded_atomic_exchange,
	resolve_overloaded_atomic_compare_exchange,
	resolve_overloaded_atomic_load, resolve_overloaded_atomic_store,
	resolve_overloaded_builtin):  Add `complain` boolean parameter
	and determine whether to emit errors based on its value.
	* c-common.h (check_builtin_function_arguments,
	resolve_overloaded_builtin):  Mention `complain` boolean
	parameter in declarations.  Give it a default of `true`.

gcc/ChangeLog:

	* config/aarch64/aarch64-c.cc
	(aarch64_resolve_overloaded_builtin,aarch64_check_builtin_call):
	Add new unused boolean parameter to match target hook
	definition.
	* config/arm/arm-builtins.cc (arm_check_builtin_call): Likewise.
	* config/arm/arm-c.cc (arm_resolve_overloaded_builtin):
	Likewise.
	* config/arm/arm-protos.h (arm_check_builtin_call): Likewise.
	* config/avr/avr-c.cc (avr_resolve_overloaded_builtin):
	Likewise.
	* config/riscv/riscv-c.cc (riscv_check_builtin_call,
	riscv_resolve_overloaded_builtin): Likewise.
	* config/rs6000/rs6000-c.cc (altivec_resolve_overloaded_builtin):
	Likewise.
	* config/rs6000/rs6000-protos.h (altivec_resolve_overloaded_builtin):
	Likewise.
	* config/s390/s390-c.cc (s390_resolve_overloaded_builtin):
	Likewise.
	* doc/tm.texi: Regenerate.
	* target.def (TARGET_RESOLVE_OVERLOADED_BUILTIN,
	TARGET_CHECK_BUILTIN_CALL): Update prototype to include a
	boolean parameter that indicates whether errors should be
	emitted.  Update documentation to mention this fact.

gcc/cp/ChangeLog:

	* call.cc (build_cxx_call):  Pass `complain` parameter to
	check_builtin_function_arguments.  Take its value from the
	`tsubst_flags_t` type `complain & tf_error`.
	* semantics.cc (finish_call_expr):  Pass `complain` parameter to
	resolve_overloaded_builtin.  Take its value from the
	`tsubst_flags_t` type `complain & tf_error`.

gcc/testsuite/ChangeLog:

	* g++.dg/template/builtin-atomic-overloads.def: New test.
	* g++.dg/template/builtin-atomic-overloads1.C: New test.
	* g++.dg/template/builtin-atomic-overloads2.C: New test.
	* g++.dg/template/builtin-atomic-overloads3.C: New test.
	* g++.dg/template/builtin-atomic-overloads4.C: New test.
	* g++.dg/template/builtin-atomic-overloads5.C: New test.
	* g++.dg/template/builtin-atomic-overloads6.C: New test.
	* g++.dg/template/builtin-atomic-overloads7.C: New test.
	* g++.dg/template/builtin-sfinae-check-function-arguments.C: New test.
	* g++.dg/template/builtin-speculation-overloads.def: New test.
	* g++.dg/template/builtin-speculation-overloads1.C: New test.
	* g++.dg/template/builtin-speculation-overloads2.C: New test.
	* g++.dg/template/builtin-speculation-overloads3.C: New test.
	* g++.dg/template/builtin-speculation-overloads4.C: New test.
	* g++.dg/template/builtin-speculation-overloads5.C: New test.
	* g++.dg/template/builtin-validate-nargs.C: New test.

Signed-off-by: Matthew Malcomson <mmalcomson@nvidia.com>
---
 gcc/c-family/c-common.cc                      | 459 +++++++++++-------
 gcc/c-family/c-common.h                       |   8 +-
 gcc/config/aarch64/aarch64-c.cc               |   6 +-
 gcc/config/arm/arm-builtins.cc                |   2 +-
 gcc/config/arm/arm-c.cc                       |   2 +-
 gcc/config/arm/arm-protos.h                   |   2 +-
 gcc/config/avr/avr-c.cc                       |   3 +-
 gcc/config/riscv/riscv-c.cc                   |   4 +-
 gcc/config/rs6000/rs6000-c.cc                 |   2 +-
 gcc/config/rs6000/rs6000-protos.h             |   3 +-
 gcc/config/s390/s390-c.cc                     |   5 +-
 gcc/cp/call.cc                                |   3 +-
 gcc/cp/semantics.cc                           |   3 +-
 gcc/doc/tm.texi                               |  11 +-
 gcc/target.def                                |  13 +-
 .../template/builtin-atomic-overloads.def     | 159 ++++++
 .../template/builtin-atomic-overloads1.C      |  24 +
 .../template/builtin-atomic-overloads2.C      |  18 +
 .../template/builtin-atomic-overloads3.C      |  19 +
 .../template/builtin-atomic-overloads4.C      |  19 +
 .../template/builtin-atomic-overloads5.C      | 329 +++++++++++++
 .../template/builtin-atomic-overloads6.C      | 171 +++++++
 .../template/builtin-atomic-overloads7.C      | 170 +++++++
 .../builtin-sfinae-check-function-arguments.C | 145 ++++++
 .../builtin-speculation-overloads.def         |  30 ++
 .../template/builtin-speculation-overloads1.C |  18 +
 .../template/builtin-speculation-overloads2.C |  19 +
 .../template/builtin-speculation-overloads3.C |  20 +
 .../template/builtin-speculation-overloads4.C |  19 +
 .../template/builtin-speculation-overloads5.C |  33 ++
 .../g++.dg/template/builtin-validate-nargs.C  |  67 +++
 31 files changed, 1595 insertions(+), 191 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-validate-nargs.C

Comments

Matthew Malcomson Oct. 21, 2024, 10:43 a.m. UTC | #1
Ping (re-sending ping because previous message body too large for list --
apologies for duplication to those on Cc).
Attaching update on testsuite to fix testism on Arm that Linaro CI caught.

> From: Matthew Malcomson <mmalcomson@nvidia.com>
> 
> This commit newly introduces the ability to use overloaded builtins in
> C++ SFINAE context.
Small update -- adding std::remove_pointer_t<T> to the first parameter
in NONPOINTER_PARAMS for some builtin-atomic-overloads.def entries.

The point of these parameter lists is to always fail due to lack of
pointer type on the first argument.  There are already a few places
where std::remove_pointer_t<T> is needed in order to ensure a type
resolution failure.

Some uses of the NONPOINTER_PARAMS only fail if the size of the types
are different (but not if the types are different directly).  These did
not fail on 64bit targets because `int *` and `int` were of different
sizes.  They failed on arm (and I was alerted thanks to the Linaro CI)
because `int *` and `int` were the same size.

This adjustment to the patch ensures we always pass a non-pointer to the
overloaded atomic builtins, ensuring we always fail for non-pointer
parameters on the check that the first argument should be a pointer.

----------  >8  -----  8<  ---------

This commit newly introduces the ability to use overloaded builtins in
C++ SFINAE context.

The goal behind this is in order to ensure there is a single mechanism
that libstdc++ can use to determine whether a given type can be used in
the atomic fetch_add (and similar) builtins.  I am working on another
patch that hopes to use this mechanism to identify whether fetch_add
(and similar) work on floating point types.

Current state of the world:

    GCC currently exposes resolved versions of these builtins to the
    user, so for GCC it's currently possible to use tests similar to the
    below to check for atomic loads on a 2 byte sized object.
      #if __has_builtin(__atomic_load_2)
    Clang does not expose resolved versions of the atomic builtins.

    clang currently allows SFINAE on builtins, so that C++ code can
    check whether a builtin is available on a given type.
    GCC does not (and that is what this patch aims to change).

    C libraries like libatomic can check whether a given atomic builtin
    can work on a given type by using autoconf to check for a
    miscompilation when attempting such a use.

My goal:
    I would like to enable floating point fetch_add (and similar) in
    GCC, in order to use those overloads in libstdc++ implementation of
    atomic<float>::fetch_add.
    This should allow compilers targeting GPU's which have floating
    point fetch_add instructions to emit optimal code.

    In order to do that I need some consistent mechanism that libstdc++
    can use to identify whether the fetch_add builtins have floating
    point overloads (and for which types these exist).

    I would hence like to enable SFINAE on builtins, so that libstdc++
    can use that mechanism for the floating point fetch_add builtins.
    Discussion with J Wakely about which approach to take for libstdc++:
    https://gcc.gnu.org/pipermail/gcc-patches/2024-October/664363.html

Implementation follows the existing mechanism for handling SFINAE
contexts in c-common.cc.  A boolean is passed into the c-common.cc
function indicating whether these functions should emit errors or not.
This boolean comes from `complain & tf_error` in the C++ frontend.
(Similar to other functions like valid_array_size_p and
c_build_vec_perm_expr).

This is done both for resolve_overloaded_builtin and
check_builtin_function_arguments, both of which can be used in SFINAE
contexts.
    N.b. I attempted to trigger something using the `reject_gcc_builtin`
    function in an SFINAE context.  Given the context where this
    function is called from the C++ frontend it looks like it may be
    possible, but I did not manage to trigger this in template context
    by attempting to do something similar to the testcases added around
    those calls.
    - I would appreciate any feedback on whether this is something that
      can happen in a template context, and if so some help writing a
      relevant testcase for it.

Both of these functions have target hooks for target specific builtins
that I have updated to take the extra boolean flag.  I have not adjusted
the functions implementing those target hooks (except to update the
declarations) so target specific builtins will still error in SFINAE
contexts.
- N.b. I could imagine not updating the target hook definition since
  nothing would use that change.  However I figure that allowing targets
  to decide this behaviour would be the right thing to do eventually,
  and since this is the target-independent part of the change to do that
  this patch should make that change.
  Could adjust if others disagree.

Other relevant points that I'd appreciate reviewers check:
- I did not pass this new flag through
  atomic_bitint_fetch_using_cas_loop since the _BitInt type is not
  available in the C++ frontend and I didn't want if conditions that can
  not be executed in the source.
- I only test non-compile-time-constant types with SVE types, since I do
  not know of a way to get a VLA into a SFINAE context.
- While writing tests I noticed a few differences with clang in this
  area.  I don't think they are problematic but am mentioning them for
  completeness and to allow others to judge if these are a problem).
  - atomic_fetch_add on a boolean is allowed by clang.
  - With SFINAE GCC is happy with multiple definitions of a differently
    typed template as long as they aren't instantiated, while clang is
    not.  This seems to be a general difference around clang and GCC and
    not specific to builtins.
    I.e. two template definitions with the same name where one
    specialises on `myfunc (std::declval<T>())` and another on
    `myfunc (std::declval<T>(), std::declval<T>())` will not give an
    error in GCC unless one attempts to instantiate the template.
    However it will give an error in clang on the template redefinition.
- I do not block the warning about using
  __builtin_speculation_safe_value on a target that does not have any
  active mitigation defined.  I do this since when this happens we do
  not return error_mark_node, which means that if a user attempted to
  use SFINAE to check for this situation that would silently fail if the
  warning were suppressed.
  N.b. this is also a warning rather than an error.
  Similarly I do not block the warning about an invalid memory model
  argument in get_atomic_generic_size.

Bootstrap and regression tested on AArch64 and x86_64.
Built first stage on targets whose target hook declaration needed
updated (though did not regtest etc).  Targets triplets I built in order
to check the backend specific changes I made:
   - arm-none-linux-gnueabihf
   - avr-linux-gnu
   - riscv-linux-gnu
   - powerpc-linux-gnu
   - s390x-linux-gnu

Ok for commit to trunk?

gcc/c-family/ChangeLog:

	* c-common.cc (builtin_function_validate_nargs,
	check_builtin_function_arguments,
	speculation_safe_value_resolve_call,
	speculation_safe_value_resolve_params, sync_resolve_size,
	sync_resolve_params, get_atomic_generic_size,
	resolve_overloaded_atomic_exchange,
	resolve_overloaded_atomic_compare_exchange,
	resolve_overloaded_atomic_load, resolve_overloaded_atomic_store,
	resolve_overloaded_builtin):  Add `complain` boolean parameter
	and determine whether to emit errors based on its value.
	* c-common.h (check_builtin_function_arguments,
	resolve_overloaded_builtin):  Mention `complain` boolean
	parameter in declarations.  Give it a default of `true`.

gcc/ChangeLog:

	* config/aarch64/aarch64-c.cc
	(aarch64_resolve_overloaded_builtin,aarch64_check_builtin_call):
	Add new unused boolean parameter to match target hook
	definition.
	* config/arm/arm-builtins.cc (arm_check_builtin_call): Likewise.
	* config/arm/arm-c.cc (arm_resolve_overloaded_builtin):
	Likewise.
	* config/arm/arm-protos.h (arm_check_builtin_call): Likewise.
	* config/avr/avr-c.cc (avr_resolve_overloaded_builtin):
	Likewise.
	* config/riscv/riscv-c.cc (riscv_check_builtin_call,
	riscv_resolve_overloaded_builtin): Likewise.
	* config/rs6000/rs6000-c.cc (altivec_resolve_overloaded_builtin):
	Likewise.
	* config/rs6000/rs6000-protos.h (altivec_resolve_overloaded_builtin):
	Likewise.
	* config/s390/s390-c.cc (s390_resolve_overloaded_builtin):
	Likewise.
	* doc/tm.texi: Regenerate.
	* target.def (TARGET_RESOLVE_OVERLOADED_BUILTIN,
	TARGET_CHECK_BUILTIN_CALL): Update prototype to include a
	boolean parameter that indicates whether errors should be
	emitted.  Update documentation to mention this fact.

gcc/cp/ChangeLog:

	* call.cc (build_cxx_call):  Pass `complain` parameter to
	check_builtin_function_arguments.  Take its value from the
	`tsubst_flags_t` type `complain & tf_error`.
	* semantics.cc (finish_call_expr):  Pass `complain` parameter to
	resolve_overloaded_builtin.  Take its value from the
	`tsubst_flags_t` type `complain & tf_error`.

gcc/testsuite/ChangeLog:

	* g++.dg/template/builtin-atomic-overloads.def: New test.
	* g++.dg/template/builtin-atomic-overloads1.C: New test.
	* g++.dg/template/builtin-atomic-overloads2.C: New test.
	* g++.dg/template/builtin-atomic-overloads3.C: New test.
	* g++.dg/template/builtin-atomic-overloads4.C: New test.
	* g++.dg/template/builtin-atomic-overloads5.C: New test.
	* g++.dg/template/builtin-atomic-overloads6.C: New test.
	* g++.dg/template/builtin-atomic-overloads7.C: New test.
	* g++.dg/template/builtin-sfinae-check-function-arguments.C: New test.
	* g++.dg/template/builtin-speculation-overloads.def: New test.
	* g++.dg/template/builtin-speculation-overloads1.C: New test.
	* g++.dg/template/builtin-speculation-overloads2.C: New test.
	* g++.dg/template/builtin-speculation-overloads3.C: New test.
	* g++.dg/template/builtin-speculation-overloads4.C: New test.
	* g++.dg/template/builtin-speculation-overloads5.C: New test.
	* g++.dg/template/builtin-validate-nargs.C: New test.

Signed-off-by: Matthew Malcomson <mmalcomson@nvidia.com>
---
 gcc/c-family/c-common.cc                      | 459 +++++++++++-------
 gcc/c-family/c-common.h                       |   8 +-
 gcc/config/aarch64/aarch64-c.cc               |   6 +-
 gcc/config/arm/arm-builtins.cc                |   2 +-
 gcc/config/arm/arm-c.cc                       |   2 +-
 gcc/config/arm/arm-protos.h                   |   2 +-
 gcc/config/avr/avr-c.cc                       |   3 +-
 gcc/config/riscv/riscv-c.cc                   |   4 +-
 gcc/config/rs6000/rs6000-c.cc                 |   2 +-
 gcc/config/rs6000/rs6000-protos.h             |   3 +-
 gcc/config/s390/s390-c.cc                     |   5 +-
 gcc/cp/call.cc                                |   3 +-
 gcc/cp/semantics.cc                           |   3 +-
 gcc/doc/tm.texi                               |  11 +-
 gcc/target.def                                |  13 +-
 .../template/builtin-atomic-overloads.def     | 177 +++++++
 .../template/builtin-atomic-overloads1.C      |  24 +
 .../template/builtin-atomic-overloads2.C      |  18 +
 .../template/builtin-atomic-overloads3.C      |  19 +
 .../template/builtin-atomic-overloads4.C      |  19 +
 .../template/builtin-atomic-overloads5.C      | 329 +++++++++++++
 .../template/builtin-atomic-overloads6.C      | 171 +++++++
 .../template/builtin-atomic-overloads7.C      | 170 +++++++
 .../builtin-sfinae-check-function-arguments.C | 145 ++++++
 .../builtin-speculation-overloads.def         |  30 ++
 .../template/builtin-speculation-overloads1.C |  18 +
 .../template/builtin-speculation-overloads2.C |  19 +
 .../template/builtin-speculation-overloads3.C |  20 +
 .../template/builtin-speculation-overloads4.C |  19 +
 .../template/builtin-speculation-overloads5.C |  33 ++
 .../g++.dg/template/builtin-validate-nargs.C  |  67 +++
 31 files changed, 1613 insertions(+), 191 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C
 create mode 100644 gcc/testsuite/g++.dg/template/builtin-validate-nargs.C

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index ec6a5da892d..90550dfbeac 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -6313,16 +6313,18 @@ check_function_arguments_recurse (void (*callback)
 
 static bool
 builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs,
-				 int required)
+				 int required, bool complain)
 {
   if (nargs < required)
     {
-      error_at (loc, "too few arguments to function %qE", fndecl);
+      if (complain)
+	error_at (loc, "too few arguments to function %qE", fndecl);
       return false;
     }
   else if (nargs > required)
     {
-      error_at (loc, "too many arguments to function %qE", fndecl);
+      if (complain)
+	error_at (loc, "too many arguments to function %qE", fndecl);
       return false;
     }
   return true;
@@ -6343,16 +6345,16 @@ builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs,
 
 bool
 check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
-				  tree fndecl, tree orig_fndecl,
-				  int nargs, tree *args)
+				  tree fndecl, tree orig_fndecl, int nargs,
+				  tree *args, bool complain)
 {
   if (!fndecl_built_in_p (fndecl))
     return true;
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
     return (!targetm.check_builtin_call
-	    || targetm.check_builtin_call (loc, arg_loc, fndecl,
-					   orig_fndecl, nargs, args));
+	    || targetm.check_builtin_call (loc, arg_loc, fndecl, orig_fndecl,
+					   nargs, args, complain));
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_FRONTEND)
     return true;
@@ -6363,9 +6365,11 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
       if (!tree_fits_uhwi_p (args[2]))
 	{
-	  error_at (ARG_LOCATION (2),
-		    "third argument to function %qE must be a constant integer",
-		    fndecl);
+	  if (complain)
+	    error_at (
+	      ARG_LOCATION (2),
+	      "third argument to function %qE must be a constant integer",
+	      fndecl);
 	  return false;
 	}
       /* fall through */
@@ -6388,17 +6392,18 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 	/* Reject invalid alignments.  */
 	if (align < BITS_PER_UNIT || maxalign < align)
 	  {
-	    error_at (ARG_LOCATION (1),
-		      "second argument to function %qE must be a constant "
-		      "integer power of 2 between %qi and %qu bits",
-		      fndecl, BITS_PER_UNIT, maxalign);
+	    if (complain)
+	      error_at (ARG_LOCATION (1),
+			"second argument to function %qE must be a constant "
+			"integer power of 2 between %qi and %qu bits",
+			fndecl, BITS_PER_UNIT, maxalign);
 	    return false;
 	  }
 	return true;
       }
 
     case BUILT_IN_CONSTANT_P:
-      return builtin_function_validate_nargs (loc, fndecl, nargs, 1);
+      return builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain);
 
     case BUILT_IN_ISFINITE:
     case BUILT_IN_ISINF:
@@ -6407,12 +6412,15 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ISNORMAL:
     case BUILT_IN_ISSIGNALING:
     case BUILT_IN_SIGNBIT:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain))
 	{
 	  if (TREE_CODE (TREE_TYPE (args[0])) != REAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (0), "non-floating-point argument in "
-			"call to function %qE", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "non-floating-point argument in "
+			  "call to function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6426,7 +6434,7 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ISLESSGREATER:
     case BUILT_IN_ISUNORDERED:
     case BUILT_IN_ISEQSIG:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2, complain))
 	{
 	  enum tree_code code0, code1;
 	  code0 = TREE_CODE (TREE_TYPE (args[0]));
@@ -6437,8 +6445,11 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 		|| ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
 		    && code1 == REAL_TYPE)))
 	    {
-	      error_at (loc, "non-floating-point arguments in call to "
-			"function %qE", fndecl);
+	      if (complain)
+		error_at (loc,
+			  "non-floating-point arguments in call to "
+			  "function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6446,20 +6457,26 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
       return false;
 
     case BUILT_IN_FPCLASSIFY:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 6))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 6, complain))
 	{
 	  for (unsigned int i = 0; i < 5; i++)
 	    if (TREE_CODE (args[i]) != INTEGER_CST)
 	      {
-		error_at (ARG_LOCATION (i), "non-const integer argument %u in "
-			  "call to function %qE", i + 1, fndecl);
+		if (complain)
+		  error_at (ARG_LOCATION (i),
+			    "non-const integer argument %u in "
+			    "call to function %qE",
+			    i + 1, fndecl);
 		return false;
 	      }
 
 	  if (TREE_CODE (TREE_TYPE (args[5])) != REAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (5), "non-floating-point argument in "
-			"call to function %qE", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (5),
+			  "non-floating-point argument in "
+			  "call to function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6467,14 +6484,18 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
       return false;
 
     case BUILT_IN_ASSUME_ALIGNED:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2 + (nargs > 2)))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2 + (nargs > 2),
+					   complain))
 	{
 	  if (nargs >= 3
 	      && TREE_CODE (TREE_TYPE (args[2])) != INTEGER_TYPE
 	      && TREE_CODE (TREE_TYPE (args[2])) != BITINT_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "non-integer argument 3 in call to "
-			"function %qE", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "non-integer argument 3 in call to "
+			  "function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6484,47 +6505,63 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_MUL_OVERFLOW:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3, complain))
 	{
 	  unsigned i;
 	  for (i = 0; i < 2; i++)
 	    if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
 	      {
-		error_at (ARG_LOCATION (i), "argument %u in call to function "
-			  "%qE does not have integral type", i + 1, fndecl);
+		if (complain)
+		  error_at (ARG_LOCATION (i),
+			    "argument %u in call to function "
+			    "%qE does not have integral type",
+			    i + 1, fndecl);
 		return false;
 	      }
 	  if (TREE_CODE (TREE_TYPE (args[2])) != POINTER_TYPE
 	      || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (args[2]))))
 	    {
-	      error_at (ARG_LOCATION (2), "argument 3 in call to function %qE "
-			"does not have pointer to integral type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument 3 in call to function %qE "
+			  "does not have pointer to integral type",
+			  fndecl);
 	      return false;
 	    }
 	  else if (TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) == ENUMERAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument 3 in call to function %qE "
-			"has pointer to enumerated type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument 3 in call to function %qE "
+			  "has pointer to enumerated type",
+			  fndecl);
 	      return false;
 	    }
 	  else if (TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) == BOOLEAN_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument 3 in call to function %qE "
-			"has pointer to boolean type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument 3 in call to function %qE "
+			  "has pointer to boolean type",
+			  fndecl);
 	      return false;
 	    }
 	  else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[2]))))
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 3, fndecl, "const",
-			TREE_TYPE (args[2]));
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  3, fndecl, "const", TREE_TYPE (args[2]));
 	      return false;
 	    }
 	  else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[2]))))
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 3, fndecl,
-			"_Atomic", TREE_TYPE (args[2]));
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  3, fndecl, "_Atomic", TREE_TYPE (args[2]));
 	      return false;
 	    }
 	  return true;
@@ -6534,26 +6571,35 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ADD_OVERFLOW_P:
     case BUILT_IN_SUB_OVERFLOW_P:
     case BUILT_IN_MUL_OVERFLOW_P:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3, complain))
 	{
 	  unsigned i;
 	  for (i = 0; i < 3; i++)
 	    if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
 	      {
-		error_at (ARG_LOCATION (i), "argument %u in call to function "
-			  "%qE does not have integral type", i + 1, fndecl);
+		if (complain)
+		  error_at (ARG_LOCATION (i),
+			    "argument %u in call to function "
+			    "%qE does not have integral type",
+			    i + 1, fndecl);
 		return false;
 	      }
 	  if (TREE_CODE (TREE_TYPE (args[2])) == ENUMERAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function "
-			"%qE has enumerated type", 3, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function "
+			  "%qE has enumerated type",
+			  3, fndecl);
 	      return false;
 	    }
 	  else if (TREE_CODE (TREE_TYPE (args[2])) == BOOLEAN_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function "
-			"%qE has boolean type", 3, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function "
+			  "%qE has boolean type",
+			  3, fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6561,32 +6607,42 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
       return false;
 
     case BUILT_IN_CLEAR_PADDING:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain))
 	{
 	  if (!POINTER_TYPE_P (TREE_TYPE (args[0])))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function "
-			"%qE does not have pointer type", 1, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function "
+			  "%qE does not have pointer type",
+			  1, fndecl);
 	      return false;
 	    }
 	  else if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (args[0]))))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function "
-			"%qE points to incomplete type", 1, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function "
+			  "%qE points to incomplete type",
+			  1, fndecl);
 	      return false;
 	    }
 	  else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[0]))))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 1, fndecl, "const",
-			TREE_TYPE (args[0]));
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  1, fndecl, "const", TREE_TYPE (args[0]));
 	      return false;
 	    }
 	  else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[0]))))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 1, fndecl,
-			"_Atomic", TREE_TYPE (args[0]));
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  1, fndecl, "_Atomic", TREE_TYPE (args[0]));
 	      return false;
 	    }
 	  return true;
@@ -6605,8 +6661,11 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 	{
 	  if (!INTEGRAL_TYPE_P (TREE_TYPE (args[1])))
 	    {
-	      error_at (ARG_LOCATION (1), "argument %u in call to function "
-			"%qE does not have integral type", 2, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (1),
+			  "argument %u in call to function "
+			  "%qE does not have integral type",
+			  2, fndecl);
 	      return false;
 	    }
 	  if ((TYPE_PRECISION (TREE_TYPE (args[1]))
@@ -6615,30 +6674,43 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 		  == TYPE_PRECISION (integer_type_node)
 		  && TYPE_UNSIGNED (TREE_TYPE (args[1]))))
 	    {
-	      error_at (ARG_LOCATION (1), "argument %u in call to function "
-			"%qE does not have %<int%> type", 2, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (1),
+			  "argument %u in call to function "
+			  "%qE does not have %<int%> type",
+			  2, fndecl);
 	      return false;
 	    }
 	}
-      else if (!builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+      else if (!builtin_function_validate_nargs (loc, fndecl, nargs, 1,
+						 complain))
 	return false;
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (args[0])))
 	{
-	  error_at (ARG_LOCATION (0), "argument %u in call to function "
-		    "%qE does not have integral type", 1, fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument %u in call to function "
+		      "%qE does not have integral type",
+		      1, fndecl);
 	  return false;
 	}
       if (TREE_CODE (TREE_TYPE (args[0])) == ENUMERAL_TYPE)
 	{
-	  error_at (ARG_LOCATION (0), "argument %u in call to function "
-		    "%qE has enumerated type", 1, fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument %u in call to function "
+		      "%qE has enumerated type",
+		      1, fndecl);
 	  return false;
 	}
       if (TREE_CODE (TREE_TYPE (args[0])) == BOOLEAN_TYPE)
 	{
-	  error_at (ARG_LOCATION (0), "argument %u in call to function "
-		    "%qE has boolean type", 1, fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument %u in call to function "
+		      "%qE has boolean type",
+		      1, fndecl);
 	  return false;
 	}
       if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FFSG
@@ -6646,15 +6718,21 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 	{
 	  if (TYPE_UNSIGNED (TREE_TYPE (args[0])))
 	    {
-	      error_at (ARG_LOCATION (0), "argument 1 in call to function "
-			"%qE has unsigned type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument 1 in call to function "
+			  "%qE has unsigned type",
+			  fndecl);
 	      return false;
 	    }
 	}
       else if (!TYPE_UNSIGNED (TREE_TYPE (args[0])))
 	{
-	  error_at (ARG_LOCATION (0), "argument 1 in call to function "
-		    "%qE has signed type", fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument 1 in call to function "
+		      "%qE has signed type",
+		      fndecl);
 	  return false;
 	}
       return true;
@@ -7235,7 +7313,8 @@ builtin_type_for_size (int size, bool unsignedp)
    the size is too large; 0 if the argument type is a pointer or the
    size if it is integral.  */
 static enum built_in_function
-speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
+speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params,
+				     bool complain)
 {
   /* Type of the argument.  */
   tree type;
@@ -7243,7 +7322,8 @@ speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
 
   if (vec_safe_is_empty (params))
     {
-      error ("too few arguments to function %qE", function);
+      if (complain)
+	error ("too few arguments to function %qE", function);
       return BUILT_IN_NONE;
     }
 
@@ -7272,7 +7352,7 @@ speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
  incompatible:
   /* Issue the diagnostic only if the argument is valid, otherwise
      it would be redundant at best and could be misleading.  */
-  if (type != error_mark_node)
+  if (type != error_mark_node && complain)
     error ("operand type %qT is incompatible with argument %d of %qE",
 	   type, 1, function);
 
@@ -7284,19 +7364,21 @@ speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
    argument, if present, must be type compatible with the first.  */
 static bool
 speculation_safe_value_resolve_params (location_t loc, tree orig_function,
-				       vec<tree, va_gc> *params)
+				       vec<tree, va_gc> *params, bool complain)
 {
   tree val;
 
   if (params->length () == 0)
     {
-      error_at (loc, "too few arguments to function %qE", orig_function);
+      if (complain)
+	error_at (loc, "too few arguments to function %qE", orig_function);
       return false;
     }
 
   else if (params->length () > 2)
     {
-      error_at (loc, "too many arguments to function %qE", orig_function);
+      if (complain)
+	error_at (loc, "too many arguments to function %qE", orig_function);
       return false;
     }
 
@@ -7306,9 +7388,9 @@ speculation_safe_value_resolve_params (location_t loc, tree orig_function,
   if (!(TREE_CODE (TREE_TYPE (val)) == POINTER_TYPE
 	|| TREE_CODE (TREE_TYPE (val)) == INTEGER_TYPE))
     {
-      error_at (loc,
-		"expecting argument of type pointer or of type integer "
-		"for argument 1");
+      if (complain)
+	error_at (loc, "expecting argument of type pointer or of type integer "
+		       "for argument 1");
       return false;
     }
   (*params)[0] = val;
@@ -7323,7 +7405,8 @@ speculation_safe_value_resolve_params (location_t loc, tree orig_function,
       if (!(TREE_TYPE (val) == TREE_TYPE (val2)
 	    || useless_type_conversion_p (TREE_TYPE (val), TREE_TYPE (val2))))
 	{
-	  error_at (loc, "both arguments must be compatible");
+	  if (complain)
+	    error_at (loc, "both arguments must be compatible");
 	  return false;
 	}
       (*params)[1] = val2;
@@ -7359,7 +7442,7 @@ speculation_safe_value_resolve_return (tree first_param, tree result)
 
 static int
 sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
-		   bool orig_format)
+		   bool orig_format, bool complain)
 {
   /* Type of the argument.  */
   tree argtype;
@@ -7369,7 +7452,8 @@ sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
 
   if (vec_safe_is_empty (params))
     {
-      error ("too few arguments to function %qE", function);
+      if (complain)
+	error ("too few arguments to function %qE", function);
       return 0;
     }
 
@@ -7410,7 +7494,7 @@ sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
  incompatible:
   /* Issue the diagnostic only if the argument is valid, otherwise
      it would be redundant at best and could be misleading.  */
-  if (argtype != error_mark_node)
+  if (argtype != error_mark_node && complain)
     error ("operand type %qT is incompatible with argument %d of %qE",
 	   argtype, 1, function);
   return 0;
@@ -7423,7 +7507,7 @@ sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
 
 static bool
 sync_resolve_params (location_t loc, tree orig_function, tree function,
-		     vec<tree, va_gc> *params, bool orig_format)
+		     vec<tree, va_gc> *params, bool orig_format, bool complain)
 {
   function_args_iterator iter;
   tree ptype;
@@ -7452,7 +7536,8 @@ sync_resolve_params (location_t loc, tree orig_function, tree function,
       ++parmnum;
       if (params->length () <= parmnum)
 	{
-	  error_at (loc, "too few arguments to function %qE", orig_function);
+	  if (complain)
+	    error_at (loc, "too few arguments to function %qE", orig_function);
 	  return false;
 	}
 
@@ -7478,7 +7563,8 @@ sync_resolve_params (location_t loc, tree orig_function, tree function,
   /* __atomic routines are not variadic.  */
   if (!orig_format && params->length () != parmnum + 1)
     {
-      error_at (loc, "too many arguments to function %qE", orig_function);
+      if (complain)
+	error_at (loc, "too many arguments to function %qE", orig_function);
       return false;
     }
 
@@ -7515,7 +7601,7 @@ sync_resolve_return (tree first_param, tree result, bool orig_format)
 
 static int
 get_atomic_generic_size (location_t loc, tree function,
-			 vec<tree, va_gc> *params)
+			 vec<tree, va_gc> *params, bool complain)
 {
   unsigned int n_param;
   unsigned int n_model;
@@ -7553,7 +7639,9 @@ get_atomic_generic_size (location_t loc, tree function,
 
   if (vec_safe_length (params) != n_param)
     {
-      error_at (loc, "incorrect number of arguments to function %qE", function);
+      if (complain)
+	error_at (loc, "incorrect number of arguments to function %qE",
+		  function);
       return 0;
     }
 
@@ -7567,24 +7655,27 @@ get_atomic_generic_size (location_t loc, tree function,
     }
   if (TREE_CODE (type_0) != POINTER_TYPE || VOID_TYPE_P (TREE_TYPE (type_0)))
     {
-      error_at (loc, "argument 1 of %qE must be a non-void pointer type",
-		function);
+      if (complain)
+	error_at (loc, "argument 1 of %qE must be a non-void pointer type",
+		  function);
       return 0;
     }
 
   if (!COMPLETE_TYPE_P (TREE_TYPE (type_0)))
     {
-      error_at (loc, "argument 1 of %qE must be a pointer to a complete type",
-		function);
+      if (complain)
+	error_at (loc, "argument 1 of %qE must be a pointer to a complete type",
+		  function);
       return 0;
     }
 
   /* Types must be compile time constant sizes. */
   if (!tree_fits_uhwi_p ((TYPE_SIZE_UNIT (TREE_TYPE (type_0)))))
     {
-      error_at (loc, 
-		"argument 1 of %qE must be a pointer to a constant size type",
-		function);
+      if (complain)
+	error_at (loc,
+		  "argument 1 of %qE must be a pointer to a constant size type",
+		  function);
       return 0;
     }
 
@@ -7593,9 +7684,10 @@ get_atomic_generic_size (location_t loc, tree function,
   /* Zero size objects are not allowed.  */
   if (size_0 == 0)
     {
-      error_at (loc, 
-		"argument 1 of %qE must be a pointer to a nonzero size object",
-		function);
+      if (complain)
+	error_at (
+	  loc, "argument 1 of %qE must be a pointer to a nonzero size object",
+	  function);
       return 0;
     }
 
@@ -7615,30 +7707,38 @@ get_atomic_generic_size (location_t loc, tree function,
 	}
       if (!POINTER_TYPE_P (type))
 	{
-	  error_at (loc, "argument %d of %qE must be a pointer type", x + 1,
-		    function);
+	  if (complain)
+	    error_at (loc, "argument %d of %qE must be a pointer type", x + 1,
+		      function);
 	  return 0;
 	}
       else if (TYPE_SIZE_UNIT (TREE_TYPE (type))
 	       && TREE_CODE ((TYPE_SIZE_UNIT (TREE_TYPE (type))))
 		  != INTEGER_CST)
 	{
-	  error_at (loc, "argument %d of %qE must be a pointer to a constant "
-		    "size type", x + 1, function);
+	  if (complain)
+	    error_at (loc,
+		      "argument %d of %qE must be a pointer to a constant "
+		      "size type",
+		      x + 1, function);
 	  return 0;
 	}
       else if (FUNCTION_POINTER_TYPE_P (type))
 	{
-	  error_at (loc, "argument %d of %qE must not be a pointer to a "
-		    "function", x + 1, function);
+	  if (complain)
+	    error_at (loc,
+		      "argument %d of %qE must not be a pointer to a "
+		      "function",
+		      x + 1, function);
 	  return 0;
 	}
       tree type_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
       size = type_size ? tree_to_uhwi (type_size) : 0;
       if (size != size_0)
 	{
-	  error_at (loc, "size mismatch in argument %d of %qE", x + 1,
-		    function);
+	  if (complain)
+	    error_at (loc, "size mismatch in argument %d of %qE", x + 1,
+		      function);
 	  return 0;
 	}
 
@@ -7650,8 +7750,11 @@ get_atomic_generic_size (location_t loc, tree function,
 	  {
 	    if (c_dialect_cxx ())
 	      {
-		error_at (loc, "argument %d of %qE must not be a pointer to "
-			  "a %<const%> type", x + 1, function);
+		if (complain)
+		  error_at (loc,
+			    "argument %d of %qE must not be a pointer to "
+			    "a %<const%> type",
+			    x + 1, function);
 		return 0;
 	      }
 	    else
@@ -7664,8 +7767,11 @@ get_atomic_generic_size (location_t loc, tree function,
 	  {
 	    if (c_dialect_cxx ())
 	      {
-		error_at (loc, "argument %d of %qE must not be a pointer to "
-			  "a %<volatile%> type", x + 1, function);
+		if (complain)
+		  error_at (loc,
+			    "argument %d of %qE must not be a pointer to "
+			    "a %<volatile%> type",
+			    x + 1, function);
 		return 0;
 	      }
 	    else
@@ -7682,8 +7788,9 @@ get_atomic_generic_size (location_t loc, tree function,
       tree p = (*params)[x];
       if (!INTEGRAL_TYPE_P (TREE_TYPE (p)))
 	{
-	  error_at (loc, "non-integer memory model argument %d of %qE", x + 1,
-		    function);
+	  if (complain)
+	    error_at (loc, "non-integer memory model argument %d of %qE", x + 1,
+		      function);
 	  return 0;
 	}
       p = fold_for_warn (p);
@@ -7773,12 +7880,13 @@ atomic_size_supported_p (int n)
    FALSE is returned if processing for the _N variation is required, and 
    NEW_RETURN is set to the return value the result is copied into.  */
 static bool
-resolve_overloaded_atomic_exchange (location_t loc, tree function, 
-				    vec<tree, va_gc> *params, tree *new_return)
+resolve_overloaded_atomic_exchange (location_t loc, tree function,
+				    vec<tree, va_gc> *params, tree *new_return,
+				    bool complain)
 {	
   tree p0, p1, p2, p3;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -7842,13 +7950,13 @@ resolve_overloaded_atomic_exchange (location_t loc, tree function,
    FALSE is returned if processing for the _N variation is required.  */
 
 static bool
-resolve_overloaded_atomic_compare_exchange (location_t loc, tree function, 
-					    vec<tree, va_gc> *params, 
-					    tree *new_return)
+resolve_overloaded_atomic_compare_exchange (location_t loc, tree function,
+					    vec<tree, va_gc> *params,
+					    tree *new_return, bool complain)
 {	
   tree p0, p1, p2;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -7924,12 +8032,13 @@ resolve_overloaded_atomic_compare_exchange (location_t loc, tree function,
    NEW_RETURN is set to the return value the result is copied into.  */
 
 static bool
-resolve_overloaded_atomic_load (location_t loc, tree function, 
-				vec<tree, va_gc> *params, tree *new_return)
+resolve_overloaded_atomic_load (location_t loc, tree function,
+				vec<tree, va_gc> *params, tree *new_return,
+				bool complain)
 {	
   tree p0, p1, p2;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -7984,12 +8093,13 @@ resolve_overloaded_atomic_load (location_t loc, tree function,
    NEW_RETURN is set to the return value the result is copied into.  */
 
 static bool
-resolve_overloaded_atomic_store (location_t loc, tree function, 
-				 vec<tree, va_gc> *params, tree *new_return)
+resolve_overloaded_atomic_store (location_t loc, tree function,
+				 vec<tree, va_gc> *params, tree *new_return,
+				 bool complain)
 {	
   tree p0, p1;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -8269,7 +8379,7 @@ atomic_bitint_fetch_using_cas_loop (location_t loc,
 
 tree
 resolve_overloaded_builtin (location_t loc, tree function,
-			    vec<tree, va_gc> *params)
+			    vec<tree, va_gc> *params, bool complain)
 {
   /* Is function one of the _FETCH_OP_ or _OP_FETCH_ built-ins?
      Those are not valid to call with a pointer to _Bool (or C++ bool)
@@ -8284,7 +8394,8 @@ resolve_overloaded_builtin (location_t loc, tree function,
       break;
     case BUILT_IN_MD:
       if (targetm.resolve_overloaded_builtin)
-	return targetm.resolve_overloaded_builtin (loc, function, params);
+	return targetm.resolve_overloaded_builtin (loc, function, params,
+						   complain);
       else
 	return NULL_TREE;
     default:
@@ -8299,13 +8410,14 @@ resolve_overloaded_builtin (location_t loc, tree function,
       {
 	tree new_function, first_param, result;
 	enum built_in_function fncode
-	  = speculation_safe_value_resolve_call (function, params);
+	  = speculation_safe_value_resolve_call (function, params, complain);
 
 	if (fncode == BUILT_IN_NONE)
 	  return error_mark_node;
 
 	first_param = (*params)[0];
-	if (!speculation_safe_value_resolve_params (loc, function, params))
+	if (!speculation_safe_value_resolve_params (loc, function, params,
+						    complain))
 	  return error_mark_node;
 
 	if (targetm.have_speculation_safe_value (true))
@@ -8324,6 +8436,11 @@ resolve_overloaded_builtin (location_t loc, tree function,
 	    /* This target doesn't have, or doesn't need, active mitigation
 	       against incorrect speculative execution.  Simply return the
 	       first parameter to the builtin.  */
+	    /* N.b. emit the warning whether or not we're given
+	       `complain`, since that argument is for things like
+	       SFINAE whereas the situation we're warning on could not be
+	       picked up by that (because we're not returning
+	       `error_mark_node`).  */
 	    if (!targetm.have_speculation_safe_value (false))
 	      /* The user has invoked __builtin_speculation_safe_value
 		 even though __HAVE_SPECULATION_SAFE_VALUE is not
@@ -8356,41 +8473,41 @@ resolve_overloaded_builtin (location_t loc, tree function,
 	  {
 	  case BUILT_IN_ATOMIC_EXCHANGE:
 	    {
-	      if (resolve_overloaded_atomic_exchange (loc, function, params,
-						      &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_EXCHANGE_N;
-	      break;
+	    if (resolve_overloaded_atomic_exchange (loc, function, params,
+						    &new_return, complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_EXCHANGE_N;
+	    break;
 	    }
 
 	  case BUILT_IN_ATOMIC_COMPARE_EXCHANGE:
 	    {
-	      if (resolve_overloaded_atomic_compare_exchange (loc, function,
-							      params,
-							      &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_COMPARE_EXCHANGE_N;
-	      break;
+	    if (resolve_overloaded_atomic_compare_exchange (loc, function,
+							    params, &new_return,
+							    complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_COMPARE_EXCHANGE_N;
+	    break;
 	    }
 	  case BUILT_IN_ATOMIC_LOAD:
 	    {
-	      if (resolve_overloaded_atomic_load (loc, function, params,
-						  &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_LOAD_N;
-	      break;
+	    if (resolve_overloaded_atomic_load (loc, function, params,
+						&new_return, complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_LOAD_N;
+	    break;
 	    }
 	  case BUILT_IN_ATOMIC_STORE:
 	    {
-	      if (resolve_overloaded_atomic_store (loc, function, params,
-						   &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_STORE_N;
-	      break;
+	    if (resolve_overloaded_atomic_store (loc, function, params,
+						 &new_return, complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_STORE_N;
+	    break;
 	    }
 	  default:
 	    gcc_unreachable ();
@@ -8442,7 +8559,8 @@ resolve_overloaded_builtin (location_t loc, tree function,
 		      && orig_code != BUILT_IN_SYNC_LOCK_TEST_AND_SET_N
 		      && orig_code != BUILT_IN_SYNC_LOCK_RELEASE_N);
 
-	int n = sync_resolve_size (function, params, fetch_op, orig_format);
+	int n = sync_resolve_size (function, params, fetch_op, orig_format,
+				   complain);
 	tree new_function, first_param, result;
 	enum built_in_function fncode;
 
@@ -8450,13 +8568,24 @@ resolve_overloaded_builtin (location_t loc, tree function,
 	  return error_mark_node;
 
 	if (n == -1)
-	  return atomic_bitint_fetch_using_cas_loop (loc, orig_code,
-						     function, params);
+	  {
+	    /* complain is related to SFINAE context.
+	       _BitInt is not defined in C++, hence can't enter this clause
+	       with complain unset.  Even if at the abstraction level
+	       this complain is unset that still makes sense (whether
+	       this function should report an error or not if anything is
+	       wrong).
+	       Since can't test avoiding an error when this value is false not
+	       writing the code and instead asserting value is not set.  */
+	    gcc_assert (complain);
+	    return atomic_bitint_fetch_using_cas_loop (loc, orig_code, function,
+						       params);
+	  }
 
 	fncode = (enum built_in_function)((int)orig_code + exact_log2 (n) + 1);
 	new_function = builtin_decl_explicit (fncode);
 	if (!sync_resolve_params (loc, function, new_function, params,
-				  orig_format))
+				  orig_format, complain))
 	  return error_mark_node;
 
 	first_param = (*params)[0];
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 027f077d51b..d2bbe9d4c20 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -857,8 +857,9 @@ extern void check_function_arguments_recurse (void (*)
 					      void *, tree,
 					      unsigned HOST_WIDE_INT,
 					      opt_code);
-extern bool check_builtin_function_arguments (location_t, vec<location_t>,
-					      tree, tree, int, tree *);
+extern bool
+check_builtin_function_arguments (location_t, vec<location_t>, tree, tree, int,
+				  tree *, bool = true);
 extern void check_function_format (const_tree, tree, int, tree *,
 				   vec<location_t> *,
 				   bool (*comp_types) (tree, tree));
@@ -1103,7 +1104,8 @@ extern tree build_function_call_vec (location_t, vec<location_t>, tree,
 				     vec<tree, va_gc> *, vec<tree, va_gc> *,
 				     tree = NULL_TREE);
 
-extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *);
+extern tree
+resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *, bool = true);
 
 extern tree finish_label_address_expr (tree, location_t);
 
diff --git a/gcc/config/aarch64/aarch64-c.cc b/gcc/config/aarch64/aarch64-c.cc
index f9b9e379375..ddf5a6c453c 100644
--- a/gcc/config/aarch64/aarch64-c.cc
+++ b/gcc/config/aarch64/aarch64-c.cc
@@ -365,8 +365,8 @@ aarch64_pragma_aarch64 (cpp_reader *)
 
 /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN.  */
 static tree
-aarch64_resolve_overloaded_builtin (unsigned int uncast_location,
-				    tree fndecl, void *uncast_arglist)
+aarch64_resolve_overloaded_builtin (unsigned int uncast_location, tree fndecl,
+				    void *uncast_arglist, bool)
 {
   vec<tree, va_gc> empty = {};
   location_t location = (location_t) uncast_location;
@@ -396,7 +396,7 @@ aarch64_resolve_overloaded_builtin (unsigned int uncast_location,
 static bool
 aarch64_check_builtin_call (location_t loc, vec<location_t> arg_loc,
 			    tree fndecl, tree orig_fndecl,
-			    unsigned int nargs, tree *args)
+			    unsigned int nargs, tree *args, bool)
 {
   unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
   unsigned int subcode = code >> AARCH64_BUILTIN_SHIFT;
diff --git a/gcc/config/arm/arm-builtins.cc b/gcc/config/arm/arm-builtins.cc
index 74cea8900b4..ee0f1c551ca 100644
--- a/gcc/config/arm/arm-builtins.cc
+++ b/gcc/config/arm/arm-builtins.cc
@@ -4211,7 +4211,7 @@ arm_general_check_builtin_call (unsigned int code)
 bool
 arm_check_builtin_call (location_t loc, vec<location_t> arg_loc,
 			tree fndecl, tree orig_fndecl,
-			unsigned int nargs, tree *args)
+			unsigned int nargs, tree *args, bool)
 {
   unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
   unsigned int subcode = code >> ARM_BUILTIN_SHIFT;
diff --git a/gcc/config/arm/arm-c.cc b/gcc/config/arm/arm-c.cc
index 6e10262b067..74006752aff 100644
--- a/gcc/config/arm/arm-c.cc
+++ b/gcc/config/arm/arm-c.cc
@@ -163,7 +163,7 @@ arm_pragma_arm (cpp_reader *)
 /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN.  */
 tree
 arm_resolve_overloaded_builtin (location_t loc, tree fndecl,
-				void *uncast_arglist)
+				void *uncast_arglist, bool)
 {
   enum resolver_ident resolver = arm_describe_resolver (fndecl);
   if (resolver == arm_cde_resolver)
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index b694589cab4..7f0f20542d6 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -32,7 +32,7 @@ extern int use_return_insn (int, rtx);
 extern bool use_simple_return_p (void);
 extern enum reg_class arm_regno_class (int);
 extern bool arm_check_builtin_call (location_t , vec<location_t> , tree,
-				    tree, unsigned int, tree *);
+				    tree, unsigned int, tree *, bool);
 extern void arm_load_pic_register (unsigned long, rtx);
 extern int arm_volatile_func (void);
 extern void arm_expand_prologue (void);
diff --git a/gcc/config/avr/avr-c.cc b/gcc/config/avr/avr-c.cc
index 81c91c38363..34531b40a9d 100644
--- a/gcc/config/avr/avr-c.cc
+++ b/gcc/config/avr/avr-c.cc
@@ -48,7 +48,8 @@ enum avr_builtin_id
 /* Implement `TARGET_RESOLVE_OVERLOADED_PLUGIN'.  */
 
 static tree
-avr_resolve_overloaded_builtin (unsigned int iloc, tree fndecl, void *vargs)
+avr_resolve_overloaded_builtin (unsigned int iloc, tree fndecl, void *vargs,
+				bool)
 {
   tree type0, type1, fold = NULL_TREE;
   avr_builtin_id id = AVR_BUILTIN_COUNT;
diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc
index 7e9c478e97b..34cc8cd96a7 100644
--- a/gcc/config/riscv/riscv-c.cc
+++ b/gcc/config/riscv/riscv-c.cc
@@ -289,7 +289,7 @@ riscv_pragma_intrinsic (cpp_reader *)
 /* Implement TARGET_CHECK_BUILTIN_CALL.  */
 static bool
 riscv_check_builtin_call (location_t loc, vec<location_t> arg_loc, tree fndecl,
-			  tree, unsigned int nargs, tree *args)
+			  tree, unsigned int nargs, tree *args, bool)
 {
   unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
   unsigned int subcode = code >> RISCV_BUILTIN_SHIFT;
@@ -308,7 +308,7 @@ riscv_check_builtin_call (location_t loc, vec<location_t> arg_loc, tree fndecl,
 /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN.  */
 static tree
 riscv_resolve_overloaded_builtin (unsigned int uncast_location, tree fndecl,
-				  void *uncast_arglist)
+				  void *uncast_arglist, bool)
 {
   vec<tree, va_gc> empty = {};
   location_t loc = (location_t) uncast_location;
diff --git a/gcc/config/rs6000/rs6000-c.cc b/gcc/config/rs6000/rs6000-c.cc
index 04882c396bf..c8369b51b84 100644
--- a/gcc/config/rs6000/rs6000-c.cc
+++ b/gcc/config/rs6000/rs6000-c.cc
@@ -1680,7 +1680,7 @@ find_instance (bool *unsupported_builtin, int *instance,
 
 tree
 altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
-				    void *passed_arglist)
+				    void *passed_arglist, bool)
 {
   rs6000_gen_builtins fcode
     = (rs6000_gen_builtins) DECL_MD_FUNCTION_CODE (fndecl);
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index b40557a8557..3328a9ebe14 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -266,7 +266,8 @@ extern unsigned int rs6000_special_round_type_align (tree, unsigned int,
 						     unsigned int);
 extern unsigned int darwin_rs6000_special_round_type_align (tree, unsigned int,
 							    unsigned int);
-extern tree altivec_resolve_overloaded_builtin (location_t, tree, void *);
+extern tree
+altivec_resolve_overloaded_builtin (location_t, tree, void *, bool = true);
 extern rtx rs6000_libcall_value (machine_mode);
 extern rtx rs6000_va_arg (tree, tree);
 extern int function_ok_for_sibcall (tree);
diff --git a/gcc/config/s390/s390-c.cc b/gcc/config/s390/s390-c.cc
index 4521a86f048..9c833547d1e 100644
--- a/gcc/config/s390/s390-c.cc
+++ b/gcc/config/s390/s390-c.cc
@@ -884,9 +884,8 @@ s390_vec_n_elem (tree fndecl)
 /* Return a tree expression for a call to the overloaded builtin
    function OB_FNDECL at LOC with arguments PASSED_ARGLIST.  */
 tree
-s390_resolve_overloaded_builtin (location_t loc,
-				 tree ob_fndecl,
-				 void *passed_arglist)
+s390_resolve_overloaded_builtin (location_t loc, tree ob_fndecl,
+				 void *passed_arglist, bool)
 {
   vec<tree, va_gc> *arglist = static_cast<vec<tree, va_gc> *> (passed_arglist);
   unsigned int in_args_num = vec_safe_length (arglist);
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 309ab01d12d..aa829704a28 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -11168,7 +11168,8 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
 	argarray[i] = maybe_constant_value (argarray[i]);
 
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
-					     orig_fndecl, nargs, argarray))
+					     orig_fndecl, nargs, argarray,
+					     complain & tf_error))
 	return error_mark_node;
       else if (fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING))
 	{
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0370d81de01..33ff7a90e6e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3195,7 +3195,8 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
       if (TREE_CODE (fn) == FUNCTION_DECL
 	  && (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL
 	      || DECL_BUILT_IN_CLASS (fn) == BUILT_IN_MD))
-	result = resolve_overloaded_builtin (input_location, fn, *args);
+	result = resolve_overloaded_builtin (input_location, fn, *args,
+					     complain & tf_error);
 
       if (!result)
 	{
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 4deb3d2c283..33a361880bc 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12108,7 +12108,7 @@ ignored.  This function should return the result of the call to the
 built-in function.
 @end deftypefn
 
-@deftypefn {Target Hook} tree TARGET_RESOLVE_OVERLOADED_BUILTIN (unsigned int @var{loc}, tree @var{fndecl}, void *@var{arglist})
+@deftypefn {Target Hook} tree TARGET_RESOLVE_OVERLOADED_BUILTIN (unsigned int @var{loc}, tree @var{fndecl}, void *@var{arglist}, bool @var{complain})
 Select a replacement for a machine specific built-in function that
 was set up by @samp{TARGET_INIT_BUILTINS}.  This is done
 @emph{before} regular type checking, and so allows the target to
@@ -12118,9 +12118,12 @@ arguments passed to the built-in function.  The result is a
 complete expression that implements the operation, usually
 another @code{CALL_EXPR}.
 @var{arglist} really has type @samp{VEC(tree,gc)*}
+@var{complain} is a boolean indicating whether invalid operations
+should emit errors.  This is set to @code{false} when the C++ templating
+context expects that errors should not be emitted (i.e. SFINAE).
 @end deftypefn
 
-@deftypefn {Target Hook} bool TARGET_CHECK_BUILTIN_CALL (location_t @var{loc}, vec<location_t> @var{arg_loc}, tree @var{fndecl}, tree @var{orig_fndecl}, unsigned int @var{nargs}, tree *@var{args})
+@deftypefn {Target Hook} bool TARGET_CHECK_BUILTIN_CALL (location_t @var{loc}, vec<location_t> @var{arg_loc}, tree @var{fndecl}, tree @var{orig_fndecl}, unsigned int @var{nargs}, tree *@var{args}, bool @var{complain})
 Perform semantic checking on a call to a machine-specific built-in
 function after its arguments have been constrained to the function
 signature.  Return true if the call is valid, otherwise report an error
@@ -12132,7 +12135,9 @@ but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}
 step is now to built-in function @var{fndecl}.  @var{loc} is the
 location of the call and @var{args} is an array of function arguments,
 of which there are @var{nargs}.  @var{arg_loc} specifies the location
-of each argument.
+of each argument.  @var{complain} is a boolean indicating whether invalid
+arguments should emitm errors.  This is set to @code{false} when the C++
+templating context expects that errors should not be emitted (i.e. SFINAE).
 @end deftypefn
 
 @deftypefn {Target Hook} tree TARGET_FOLD_BUILTIN (tree @var{fndecl}, int @var{n_args}, tree *@var{argp}, bool @var{ignore})
diff --git a/gcc/target.def b/gcc/target.def
index b3155010888..66c14dfc355 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2496,8 +2496,11 @@ declaration of the built-in function.  @var{arglist} is the list of\n\
 arguments passed to the built-in function.  The result is a\n\
 complete expression that implements the operation, usually\n\
 another @code{CALL_EXPR}.\n\
-@var{arglist} really has type @samp{VEC(tree,gc)*}",
- tree, (unsigned int /*location_t*/ loc, tree fndecl, void *arglist), NULL)
+@var{arglist} really has type @samp{VEC(tree,gc)*}\n\
+@var{complain} is a boolean indicating whether invalid operations\n\
+should emit errors.  This is set to @code{false} when the C++ templating\n\
+context expects that errors should not be emitted (i.e. SFINAE).",
+ tree, (unsigned int /*location_t*/ loc, tree fndecl, void *arglist, bool complain), NULL)
 
 DEFHOOK
 (check_builtin_call,
@@ -2512,9 +2515,11 @@ but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}\n\
 step is now to built-in function @var{fndecl}.  @var{loc} is the\n\
 location of the call and @var{args} is an array of function arguments,\n\
 of which there are @var{nargs}.  @var{arg_loc} specifies the location\n\
-of each argument.",
+of each argument.  @var{complain} is a boolean indicating whether invalid\n\
+arguments should emitm errors.  This is set to @code{false} when the C++\n\
+templating context expects that errors should not be emitted (i.e. SFINAE).",
  bool, (location_t loc, vec<location_t> arg_loc, tree fndecl,
-	tree orig_fndecl, unsigned int nargs, tree *args),
+	tree orig_fndecl, unsigned int nargs, tree *args, bool complain),
  NULL)
 
 /* Fold a target-specific builtin to a tree valid for both GIMPLE
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def
new file mode 100644
index 00000000000..8df11f57420
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def
@@ -0,0 +1,177 @@
+#include <type_traits>
+
+class X{};
+/* Want a zero-sized type in order to trigger one of the error messages.
+   Don't want the error message about creating a zero sized type.
+   However, *do* want to see any pedantic error messages coming from the rest
+   of the testcase.  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+class Zero {
+    unsigned int trailing[0];
+};
+#pragma GCC diagnostic pop
+class Large { public: int arr[10]; };
+class Incomplete;
+
+/* N.b. we have to use std::remove_pointer_t<T> for a few of the cases below to
+   handle using `int *` as the type.
+   In turn the problem is:
+     1) __atomic_load_n (int *, int) is valid, so when having `int *` as the
+	type to operate on does not create something invalid (which is the
+	point of the NONPOINTER_PARAMS entry).
+     2) __atomic_store_n (int *, int *, int) is not correct w.r.t. types
+	according to the GCC manual (and indeed ends up casting the pointer VAL
+	into an integer before storing it into the location.
+	However this is a known behaviour (PR 69404) so we're just working
+	around that for the moment.
+     3) __atomic_load, __atomic_store, __atomic_exchange, __atomic_exchange_n,
+       __atomic_compare_exchange, and __atomic_compare_exchange_n, and all the
+	__atomic_fetch_<op> functions are using std::remove_pointer_t for
+	essentially the same behaviour of discarding the types as
+	__atomic_store_n.  */
+
+#define ATOMIC_SFINAES                                                         \
+  SFINAE_TYPE_CHECK (load_n, (std::declval<T *> (), int ()),                   \
+		     (std::declval<std::remove_pointer_t<T>> (), int ()))      \
+  SFINAE_TYPE_CHECK (load,                                                     \
+		     (std::declval<T *> (), std::declval<T *> (), int ()),     \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T *> (), int ()))                           \
+  SFINAE_TYPE_CHECK (store_n,                                                  \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (store,                                                    \
+		     (std::declval<T *> (), std::declval<T *> (), int ()),     \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T *> (), int ()))                           \
+  SFINAE_TYPE_CHECK (exchange_n,                                               \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (exchange,                                                 \
+		     (std::declval<T *> (), std::declval<T *> (),              \
+		      std::declval<T *> (), int ()),                           \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T *> (), std::declval<T *> (), int ()))     \
+  SFINAE_TYPE_CHECK (compare_exchange_n,                                       \
+		     (std::declval<T *> (), std::declval<T *> (),              \
+		      std::declval<T> (), bool (), int (), int ()),            \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T *> (), std::declval<T> (), bool (),       \
+		      int (), int ()))                                         \
+  SFINAE_TYPE_CHECK (compare_exchange,                                         \
+		     (std::declval<T *> (), std::declval<T *> (),              \
+		      std::declval<T *> (), bool (), int (), int ()),          \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T *> (), std::declval<T *> (), bool (),     \
+		      int (), int ()))                                         \
+  SFINAE_TYPE_CHECK (add_fetch,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (fetch_add,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (sub_fetch,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (fetch_sub,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (and_fetch,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (fetch_and,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (xor_fetch,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (fetch_xor,                                                \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (or_fetch,                                                 \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (fetch_or,                                                 \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (nand_fetch,                                               \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))                             \
+  SFINAE_TYPE_CHECK (fetch_nand,                                               \
+		     (std::declval<T *> (), std::declval<T> (), int ()),       \
+		     (std::declval<std::remove_pointer_t<T>> (),               \
+		      std::declval<T> (), int ()))
+
+ATOMIC_SFINAES
+
+#define FETCH_OP_ASSERTS(NAME) \
+  MAKE_ATOMIC_ASSERT(NAME, int *, true) \
+  MAKE_ATOMIC_ASSERT(NAME, float, false) \
+  MAKE_ATOMIC_ASSERT(NAME, int, true) \
+  MAKE_ATOMIC_ASSERT(NAME, bool,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, X,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Zero,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Large,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Incomplete,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, long, true)
+
+#define ATOMIC_FETCH_ASSERTS \
+  FETCH_OP_ASSERTS(add_fetch) \
+  FETCH_OP_ASSERTS(fetch_add) \
+  FETCH_OP_ASSERTS(sub_fetch) \
+  FETCH_OP_ASSERTS(fetch_sub) \
+  FETCH_OP_ASSERTS(and_fetch) \
+  FETCH_OP_ASSERTS(fetch_and) \
+  FETCH_OP_ASSERTS(xor_fetch) \
+  FETCH_OP_ASSERTS(fetch_xor) \
+  FETCH_OP_ASSERTS(or_fetch) \
+  FETCH_OP_ASSERTS(fetch_or) \
+  FETCH_OP_ASSERTS(nand_fetch) \
+  FETCH_OP_ASSERTS(fetch_nand)
+
+#define ATOMIC_GENERIC_ASSERTS(NAME) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, int *, true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, int, true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, bool,  true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, X,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, Zero,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, Large,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, Incomplete,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, long, true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, float, false) \
+  MAKE_ATOMIC_ASSERT(NAME, int *, true) \
+  MAKE_ATOMIC_ASSERT(NAME, int, true) \
+  MAKE_ATOMIC_ASSERT(NAME, bool,  true) \
+  MAKE_ATOMIC_ASSERT(NAME, X,  true) \
+  MAKE_ATOMIC_ASSERT(NAME, Zero,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Large,  true) \
+  MAKE_ATOMIC_ASSERT(NAME, Incomplete,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, float, true) \
+  MAKE_ATOMIC_ASSERT(NAME, long, true)
+
+
+#define ATOMIC_ASSERTS \
+  ATOMIC_FETCH_ASSERTS \
+  ATOMIC_GENERIC_ASSERTS(load) \
+  ATOMIC_GENERIC_ASSERTS(store) \
+  ATOMIC_GENERIC_ASSERTS(exchange) \
+  ATOMIC_GENERIC_ASSERTS(compare_exchange)
+
+int main() {
+    ATOMIC_ASSERTS
+    return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C
new file mode 100644
index 00000000000..b6b06c725f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C
@@ -0,0 +1,24 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* builtin-atomic-overloads{1,2,3,4,5}.C are focussed on checking various
+   properties of all the different atomic builtins.
+   builtin-atomic-overloads6.C is focussed on checking all error conditions in
+   the code ignoring which builtin we trigger them with.  */
+
+/* Checks performed here:
+   Correctly specified -- as long as the type is something that these builtins
+   can work on.  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME PARAMS) >> \
+    : std::true_type {};
+
+/* Success according to type argument.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == SUCCESS);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C
new file mode 100644
index 00000000000..72131f5cc2b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C
@@ -0,0 +1,18 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Parameters without a pointer where it should be.  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME NONPOINTER_PARAMS) >> \
+    : std::true_type {};
+
+/* Everything fails with pointer to non-pointer mismatch.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == false);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C
new file mode 100644
index 00000000000..e872cc4b553
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C
@@ -0,0 +1,19 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Too many arguments (all atomic builtins take less than 7 arguments).  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME \
+			(int(), int(), int(), int(), int(), int(), std::declval<T>())) >> \
+  : std::true_type {};
+
+/* Everything fails with too many arguments.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == false);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C
new file mode 100644
index 00000000000..7fd3dcb56aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C
@@ -0,0 +1,19 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Too few arguments (all atomic functions require more than one argument).  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (std::declval<T>())) >> \
+    : std::true_type {};
+
+
+/* Everything fails with too few arguments.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == false);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C
new file mode 100644
index 00000000000..44ada61a574
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C
@@ -0,0 +1,329 @@
+/* Check that overloaded builtins still error when not in SFINAE context.  */
+// { dg-do compile { target c++17 } }
+#include <type_traits>
+
+/* Should error here due to the fully specified (and invalid) builtin calls.  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (X(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (Incomplete(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int(), \
+					  int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (std::declval<int*>(), int(), int(), int(), \
+					  int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME ()) >> \
+    : std::true_type {};
+
+/* All the errors that are emitted.  We don't check that the errors directly
+   correspond to the relevant scenarios because validation that the correct
+   errors are generated for the relevant problems is done in other tests.
+
+   This test is checking that all the error types below are still emitted
+   when the problem occurs in templates for fully specified calls.
+
+   NOTE: We are missing some of the errors that could be emitted because the
+   above doesn't generate all invalid calls.
+   Things that could be added:
+      - pointer to incomplete type
+      - pointer to type of non-constant size
+      - pointer to type of zero size
+      - arguments after the first one:
+	- not pointers
+	- pointers to non-constant sized type
+	- pointers to function
+	- pointer to type of different size to the first one.
+	- pointer to const type
+	- pointer to volatile type
+      - memory model argument is not an integral type
+      - all errors around bitint
+      */
+
+/* { dg-error "argument 1 of '__atomic_compare_exchange' must be a non-void pointer type"                  "" { target *-*-* } 31 } */
+/* { dg-error "argument 1 of '__atomic_exchange' must be a non-void pointer type"                          "" { target *-*-* } 23 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a non-void pointer type"                              "" { target *-*-* } 19 } */
+/* { dg-error "argument 1 of '__atomic_store' must be a non-void pointer type"                             "" { target *-*-* } 19 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 19 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 23 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 53 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 19 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 31 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 53 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 23 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 31 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 53 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 23 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 31 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 53 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_add_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_and_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_compare_exchange_n'" "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_exchange_n'"         "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_add'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_and'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_nand'"         "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_or'"           "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_sub'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_xor'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_load_n'"             "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_nand_fetch'"         "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_or_fetch'"           "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_store_n'"            "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_sub_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_xor_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_add_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_and_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_compare_exchange_n'"          "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_exchange_n'"                  "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_add'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_and'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_nand'"                  "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_or'"                    "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_sub'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_xor'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_load_n'"                      "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_nand_fetch'"                  "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_or_fetch'"                    "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_store_n'"                     "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_sub_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_xor_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "too few arguments to function '__atomic_add_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_and_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_compare_exchange_n'"                                "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_exchange_n'"                                        "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_add'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_and'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_nand'"                                        "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_or'"                                          "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_sub'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_xor'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_load_n'"                                            "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_nand_fetch'"                                        "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_or_fetch'"                                          "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_store_n'"                                           "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_sub_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_xor_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too many arguments to function '__atomic_add_fetch'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_and_fetch'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_compare_exchange_n'"                               "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_exchange_n'"                                       "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_add'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_and'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_nand'"                                       "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_or'"                                         "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_sub'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_xor'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_load_n'"                                           "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_nand_fetch'"                                       "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_or_fetch'"                                         "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_store_n'"                                          "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_sub_fetch'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_xor_fetch'"                                        "" { target *-*-* } 48 } */
+
+
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 11 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 15 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 19 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 23 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 27 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 31 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 35 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 39 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 43 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 48 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 53 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 11 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 15 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 19 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 23 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 27 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 31 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 35 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 39 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 44 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 49 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 53 } */
+
+/* Just avoid generating anything for the assertions (not what we're testing
+   here).  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS)
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C
new file mode 100644
index 00000000000..6ecf318b8c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C
@@ -0,0 +1,171 @@
+/* Various atomic builtin errors are still emitted when in a fully specified
+   builtin in a template.  */
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-Wno-pedantic" }
+#include <type_traits>
+#ifdef __ARM_FEATURE_SVE
+#include <arm_sve.h>
+#endif
+
+/*
+   N.b. covering all if clauses, *not* all possible errors.
+   E.g. load, store, exchange, compare_exchange all go through
+   get_atomic_generic_size.  I ensure I test all if clauses in that function
+   but do not ensure each clause is hit when using each of the different
+   builtins.
+
+   N.b. this is the stuff that is not handled by
+   builtin-atomic-overloads{1,2,3,4,5}.C  */ 
+
+class X{};
+/* Want a zero-sized type in order to trigger one of the error messages.
+   Don't want the error message about creating a zero sized type.
+   However, *do* want to see any pedantic error messages coming from the rest
+   of the testcase.  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+class Zero {
+    unsigned int trailing[0];
+};
+#pragma GCC diagnostic pop
+class Large { public: int arr[10]; };
+class Incomplete;
+/* N.b. If there are other non-constant size types that I can use in a template
+   would appreciate hearing about it (especially if they work on all targets).
+   AFAIK VLA's are the other canonical example and have not managed to trigger
+   the same error with those due to scoping limitations.  */
+#ifdef __ARM_FEATURE_SVE
+    typedef __SVUint32_t NonConstant;
+#else
+class NonConstant { };
+#endif
+typedef __UINT64_TYPE__ uint64_t;
+typedef __UINT32_TYPE__ uint32_t;
+// typedef _BitInt(12) Bitint;
+
+
+#define INCORRECT_NUMBER_ARGUMENTS(X) \
+  X(load, (int(), int(), int(), int()), 0)
+
+#define NONPOINTER_FIRST_ARG(X) \
+  X(load, (int(), int(), int()), 1)
+#define INCOMPLETE_FIRST_ARG(X) \
+  X(load, (Incomplete(), int(), int()), 2)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_FIRST_ARG(X) \
+  X(load, (std::declval<NonConstant*>(), std::declval<NonConstant*>(), int()), 3)
+#define ZEROSIZE_FIRST_ARG(X) \
+  X(load, (std::declval<Zero*>(), std::declval<Zero*>(), int()), 4)
+
+// Errors triggered by a bad type in the first position not yet triggered by
+// builtin-atomic-overloads5.C.
+// N.b. these are already checked to *not* give an error in the SFINAE context
+// by builtin-atomic-overloads1.C.
+#define FIRST_ARGS_BADTYPE(X) \
+  ZEROSIZE_FIRST_ARG(X) \
+  NONCONST_SIZE_FIRST_ARG(X) \
+  INCOMPLETE_FIRST_ARG(X) \
+  NONPOINTER_FIRST_ARG(X)
+
+#define NONPOINTER_OTHER_ARG(X) \
+  X(load, (std::declval<int*>(), int(), int()), 5)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_OTHER_ARG(X) \
+  X(load, (std::declval<int*>(), std::declval<NonConstant*>(), int()), 6)
+#define FUNCTIONPTR_OTHER_ARG(X) \
+  X(load, (std::declval<int*>(), std::declval<int(*)()>(), int()), 7)
+#define SIZE_MISMATCH(X) \
+  X(load, (std::declval<uint32_t*>(), std::declval<uint64_t*>(), int()), 8)
+#define OUTPUT_CONST(X) \
+  X(load, (std::declval<int*>(), std::declval<const int*>(), int()), 9)
+#define SECOND_VOLATILE(X) \
+  X(load, (std::declval<int*>(), std::declval<volatile int*>(), int()), 10)
+
+#define OTHER_ARG_BADTYPE(X) \
+  NONPOINTER_OTHER_ARG(X) \
+  SECOND_VOLATILE(X) \
+  OUTPUT_CONST(X) \
+  SIZE_MISMATCH(X) \
+  FUNCTIONPTR_OTHER_ARG(X) \
+  NONCONST_SIZE_OTHER_ARG(X)
+
+#define MEMMODEL_BADTYPE(X) \
+  X(load, (std::declval<int*>(), std::declval<int*>(), float()), 11)
+#define MEMMODEL_TOOLARGE(X) \
+  X(load, (std::declval<int*>(), std::declval<int*>(), 100), 12)
+
+#define MEMMODEL_BAD(X) \
+  MEMMODEL_BADTYPE(X) \
+  MEMMODEL_TOOLARGE(X)
+
+#define GET_ATOMIC_GENERIC_ERRS(X) \
+  INCORRECT_NUMBER_ARGUMENTS(X) \
+  FIRST_ARGS_BADTYPE(X) \
+  OTHER_ARG_BADTYPE(X) \
+  MEMMODEL_BAD(X)
+
+#define SYNC_SIZE_TOOFEW(X) \
+  X(load_n, (), 0)
+#define SYNC_SIZE_INCOMPATIBLE(X) \
+  X(load_n, (int(), int()), 1)
+#define SYNC_SIZE_ERRS(X) \
+  SYNC_SIZE_TOOFEW(X) \
+  SYNC_SIZE_INCOMPATIBLE(X)
+
+#define SYNC_PARM_TOOFEW(X) \
+  X(load_n, (std::declval<int*>()), 2)
+#define SYNC_PARM_TOOMANY(X) \
+  X(load_n, (std::declval<int*>(), int(), int()), 3)
+#define SYNC_PARM_ERRS(X) \
+  SYNC_PARM_TOOFEW(X) \
+  SYNC_PARM_TOOMANY(X)
+
+/*
+   No Bitint in C++.  Hence can't check for this error.
+#define BITINT_FETCHCAS_TOOFEW(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>()))
+#define BITINT_FETCHCAS_TOOMANY(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>(), int(), int()))
+#define BITINT_FETCHCAS_ERRS(X) \
+  BITINT_FETCHCAS_TOOFEW(X) \
+  BITINT_FETCHCAS_TOOMANY(X)
+*/
+#define BITINT_FETCHCAS_ERRS(X)
+
+#define ALL_ERRS(X) \
+  GET_ATOMIC_GENERIC_ERRS(X) \
+  SYNC_SIZE_ERRS(X) \
+  SYNC_PARM_ERRS(X) \
+  BITINT_FETCHCAS_ERRS(X)
+
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, COUNTER) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available_##COUNTER : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available_##COUNTER<T, \
+    std::void_t<decltype(__atomic_##NAME PARAMS) >> \
+    : std::true_type {}; \
+
+ALL_ERRS(SFINAE_TYPE_CHECK)
+
+/* { dg-error "too few arguments to function '__atomic_load_n'"                          "" { target *-*-* } 108 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"  "" { target *-*-* } 110 } */
+/* { dg-error "too few arguments to function '__atomic_load_n'"                          "" { target *-*-* } 116 } */
+/* { dg-error "too many arguments to function '__atomic_load_n'"                         "" { target *-*-* } 118 } */
+/* { dg-error "template argument 1 is invalid"                                           "" { target *-*-* } 146 } */
+/* { dg-error "template argument 2 is invalid"                                           "" { target *-*-* } 146 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                "" { target *-*-* } 48 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a non-void pointer type"            "" { target *-*-* } 51 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a non-void pointer type"            "" { target *-*-* } 53 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a pointer to a constant size type"  "" { target aarch64_sve } 56 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a pointer to a nonzero size object" "" { target *-*-* } 58 } */
+/* { dg-error "argument 2 of '__atomic_load' must be a pointer type"                     "" { target *-*-* } 71 } */
+/* { dg-error "argument 2 of '__atomic_load' must be a pointer to a constant size type"  "" { target aarch64_sve } 74 } */
+/* { dg-error "size mismatch in argument 2 of '__atomic_load'"                           "" { target { ! aarch64_sve } } 74 } */
+/* { dg-error "argument 2 of '__atomic_load' must not be a pointer to a function"        "" { target *-*-* } 76 } */
+/* { dg-error "size mismatch in argument 2 of '__atomic_load'"                           "" { target *-*-* } 78 } */
+/* { dg-error "argument 2 of '__atomic_load' must not be a pointer to a 'const' type"    "" { target *-*-* } 80 } */
+/* { dg-error "argument 2 of '__atomic_load' must not be a pointer to a 'volatile' type" "" { target *-*-* } 82 } */
+/* { dg-error "non-integer memory model argument 3 of '__atomic_load'"                   "" { target *-*-* } 93 } */
+
+/* { dg-warning {invalid memory model argument 3 of '__atomic_load' \[-Winvalid-memory-model\]} "" { target *-*-* } 95 } */
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C
new file mode 100644
index 00000000000..ef1d4627758
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C
@@ -0,0 +1,170 @@
+/* Various atomic builtin errors not emitted when in SFINAE context.  */
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-Wno-pedantic" }
+#include <type_traits>
+#ifdef __ARM_FEATURE_SVE
+#include <arm_sve.h>
+#endif
+
+/*
+   N.b. covering all if clauses, *not* all possible errors.
+   E.g. load, store, exchange, compare_exchange all go through
+   get_atomic_generic_size.  I ensure I test all if clauses in that function
+   but do not ensure each clause is hit when using each of the different
+   builtins.
+
+   N.b. this is the stuff that is not handled by
+   builtin-atomic-overloads{1,2,3,4,5}.C  */ 
+
+class X{};
+/* Want a zero-sized type in order to trigger one of the error messages.
+   Don't want the error message about creating a zero sized type.
+   However, *do* want to see any pedantic error messages coming from the rest
+   of the testcase (shouldn't be any, but would like to be alerted if there
+   are).  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+class Zero {
+    unsigned int trailing[0];
+};
+#pragma GCC diagnostic pop
+class Large { public: int arr[10]; };
+class Incomplete;
+/* N.b. If there are other non-constant size types that I can use in a template
+   would appreciate hearing about it (especially if they work on all targets).
+   AFAIK VLA's are the other canonical example and have not managed to trigger
+   the same error with those due to scoping limitations.  */
+#ifdef __ARM_FEATURE_SVE
+    typedef __SVUint32_t NonConstant;
+#else
+class NonConstant { };
+#endif
+typedef __UINT64_TYPE__ uint64_t;
+typedef __UINT32_TYPE__ uint32_t;
+// typedef _BitInt(12) Bitint;
+
+
+#define INCORRECT_NUMBER_ARGUMENTS(X) \
+  X(load, (std::declval<T>(), int(), int(), int()), 0)
+
+#define NONPOINTER_FIRST_ARG(X) \
+  X(load, (std::declval<std::remove_pointer_t<T>>(), int(), int()), 1)
+#define INCOMPLETE_FIRST_ARG(X) \
+  X(load, (std::declval<Incomplete*>(), std::declval<T>(), int()), 2)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_FIRST_ARG(X) \
+  X(load, (std::declval<NonConstant*>(), std::declval<T>(), int()), 3)
+#define ZEROSIZE_FIRST_ARG(X) \
+  X(load, (std::declval<Zero*>(), std::declval<T>(), int()), 4)
+
+// Errors triggered by a bad type in the first position not yet triggered by
+// builtin-atomic-overloads5.C.
+// N.b. these are already checked to *not* give an error in the SFINAE context
+// by builtin-atomic-overloads1.C.
+#define FIRST_ARGS_BADTYPE(X) \
+  ZEROSIZE_FIRST_ARG(X) \
+  NONCONST_SIZE_FIRST_ARG(X) \
+  INCOMPLETE_FIRST_ARG(X) \
+  NONPOINTER_FIRST_ARG(X)
+
+#define NONPOINTER_OTHER_ARG(X) \
+  X(load, (std::declval<T>(), int(), int()), 5)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_OTHER_ARG(X) \
+  X(load, (std::declval<T>(), std::declval<NonConstant*>(), int()), 6)
+#define FUNCTIONPTR_OTHER_ARG(X) \
+  X(load, (std::declval<T>(), std::declval<int(*)()>(), int()), 7)
+#define SIZE_MISMATCH(X) \
+  X(load, (std::declval<T>(), std::declval<uint64_t*>(), int()), 8)
+#define OUTPUT_CONST(X) \
+  X(load, (std::declval<T>(), std::declval<const int*>(), int()), 9)
+#define SECOND_VOLATILE(X) \
+  X(load, (std::declval<T>(), std::declval<volatile int*>(), int()), 10)
+
+#define OTHER_ARG_BADTYPE(X) \
+  NONPOINTER_OTHER_ARG(X) \
+  SECOND_VOLATILE(X) \
+  OUTPUT_CONST(X) \
+  SIZE_MISMATCH(X) \
+  FUNCTIONPTR_OTHER_ARG(X) \
+  NONCONST_SIZE_OTHER_ARG(X)
+
+#define MEMMODEL_BADTYPE(X) \
+  X(load, (std::declval<T>(), std::declval<int*>(), float()), 11)
+/* Don't include this in ALL_ERRS since this is a warning should still resolve
+   to `true` so we need a different ASSERT macro for it. */
+/* { dg-warning {invalid memory model argument 3 of '__atomic_load' \[-Winvalid-memory-model\]} "" { target *-*-* } .+2 } */
+#define MEMMODEL_TOOLARGE(X) \
+  X(load, (std::declval<T>(), std::declval<int*>(), 100), 12)
+
+#define MEMMODEL_BAD(X) \
+  MEMMODEL_BADTYPE(X) \
+
+#define GET_ATOMIC_GENERIC_ERRS(X) \
+  INCORRECT_NUMBER_ARGUMENTS(X) \
+  FIRST_ARGS_BADTYPE(X) \
+  OTHER_ARG_BADTYPE(X) \
+  MEMMODEL_BAD(X)
+
+/*  Can't trigger this error in SFINAE context since in order to trigger error
+    need zero arguments, but that means type is fully specified.
+    
+#define SYNC_SIZE_TOOFEW(X) \
+  X(load_n, (), 0)
+  */
+#define SYNC_SIZE_TOOFEW(X)
+#define SYNC_SIZE_INCOMPATIBLE(X) \
+  X(load_n, (int(), std::declval<T>()), 1)
+#define SYNC_SIZE_ERRS(X) \
+  SYNC_SIZE_TOOFEW(X) \
+  SYNC_SIZE_INCOMPATIBLE(X)
+
+#define SYNC_PARM_TOOFEW(X) \
+  X(load_n, (std::declval<T>()), 2)
+#define SYNC_PARM_TOOMANY(X) \
+  X(load_n, (std::declval<T>(), int(), int()), 3)
+#define SYNC_PARM_ERRS(X) \
+  SYNC_PARM_TOOFEW(X) \
+  SYNC_PARM_TOOMANY(X)
+
+/*
+   No Bitint in C++.  Hence can't check for this error.
+#define BITINT_FETCHCAS_TOOFEW(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>()))
+#define BITINT_FETCHCAS_TOOMANY(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>(), int(), int()))
+#define BITINT_FETCHCAS_ERRS(X) \
+  BITINT_FETCHCAS_TOOFEW(X) \
+  BITINT_FETCHCAS_TOOMANY(X)
+*/
+#define BITINT_FETCHCAS_ERRS(X)
+
+#define ALL_ERRS(X) \
+  GET_ATOMIC_GENERIC_ERRS(X) \
+  SYNC_SIZE_ERRS(X) \
+  SYNC_PARM_ERRS(X) \
+  BITINT_FETCHCAS_ERRS(X)
+
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, COUNTER) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available_##COUNTER : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available_##COUNTER<T, \
+    std::void_t<decltype(__atomic_##NAME PARAMS) >> \
+    : std::true_type {}; \
+
+ALL_ERRS(SFINAE_TYPE_CHECK)
+MEMMODEL_TOOLARGE(SFINAE_TYPE_CHECK)
+
+#define ASSERT(NAME, PARAMS, COUNTER) \
+  static_assert(is_##NAME##_available_##COUNTER<int*>::value == false);
+
+#define ASSERT_TRUE(NAME, PARAMS, COUNTER) \
+  static_assert(is_##NAME##_available_##COUNTER<int*>::value == true);
+
+int foo() {
+    ALL_ERRS(ASSERT)
+    MEMMODEL_TOOLARGE(ASSERT_TRUE)
+    return 1;
+}
+
diff --git a/gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C b/gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C
new file mode 100644
index 00000000000..1dbf4d7df0c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C
@@ -0,0 +1,145 @@
+/* Testing various builtins don't complain about incorrect number of arguments.  */
+// { dg-do compile { target c++17 } }
+#include <type_traits>
+
+/* Here checking the check_builtin_function_arguments function doesn't error
+   in an SFINAE context.  Test each of the errors that are directly emitted
+   from check_builtin_function_arguments.  */
+
+class BasicClass { };
+class Incomplete;
+enum En { En_A };
+
+/* N.b. Do not include tests against the *_overflow error message of storing to
+   atomic types since I don't know how to make a type that is both TYPE_ATOMIC
+   and INTEGRAL_TYPE_P in C++ (where _Atomic is not a keyword).
+   Similar for clear_padding error message on _Atomic integral types.  */
+#define NARGS_CHECKS(X)                                                        \
+  X (clrsbg, (std::declval<T> ()), unsigned, 4)                                \
+  X (ffsg, (std::declval<T> ()), unsigned, 4)                                  \
+  X (clzg, (std::declval<T> ()), int, 4)                                       \
+  X (ctzg, (std::declval<T> ()), int, 4)                                       \
+  X (parityg, (std::declval<T> ()), int, 4)                                    \
+  X (popcountg, (std::declval<T> ()), int, 4)                                  \
+  X (clzg, (std::declval<T> ()), bool, 3)                                      \
+  X (ctzg, (std::declval<T> ()), bool, 3)                                      \
+  X (clrsbg, (std::declval<T> ()), bool, 3)                                    \
+  X (ffsg, (std::declval<T> ()), bool, 3)                                      \
+  X (parityg, (std::declval<T> ()), bool, 3)                                   \
+  X (popcountg, (std::declval<T> ()), bool, 3)                                 \
+  X (clzg, (std::declval<T> ()), En, 2)                                        \
+  X (ctzg, (std::declval<T> ()), En, 2)                                        \
+  X (clrsbg, (std::declval<T> ()), En, 2)                                      \
+  X (ffsg, (std::declval<T> ()), En, 2)                                        \
+  X (parityg, (std::declval<T> ()), En, 2)                                     \
+  X (popcountg, (std::declval<T> ()), En, 2)                                   \
+  X (clzg, (std::declval<T> ()), float, 1)                                     \
+  X (ctzg, (std::declval<T> ()), float, 1)                                     \
+  X (clrsbg, (std::declval<T> ()), float, 1)                                   \
+  X (ffsg, (std::declval<T> ()), float, 1)                                     \
+  X (parityg, (std::declval<T> ()), float, 1)                                  \
+  X (popcountg, (std::declval<T> ()), float, 1)                                \
+  X (clzg, (std::declval<T> (), std::declval<long> ()), int, 101)              \
+  X (ctzg, (std::declval<T> (), std::declval<long> ()), int, 101)              \
+  X (clzg, (std::declval<T> (), std::declval<T> ()), float, 100)               \
+  X (ctzg, (std::declval<T> (), std::declval<T> ()), float, 100)               \
+  X (clear_padding, (std::declval<T *> ()), const int, 3)                      \
+  X (clear_padding, (std::declval<T *> ()), Incomplete, 2)                     \
+  X (clear_padding, (std::declval<T> ()), int, 1)                              \
+  X (add_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool> ()), int, 3)  \
+  X (sub_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool> ()), int, 3)  \
+  X (mul_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool> ()), int, 3)  \
+  X (add_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<En> ()), int, 2)    \
+  X (sub_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<En> ()), int, 2)    \
+  X (mul_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<En> ()), int, 2)    \
+  X (add_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), float, 1)   \
+  X (sub_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), float, 1)   \
+  X (mul_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), float, 1)   \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<const int *> ()),   \
+     int, 5)                                                                   \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<const int *> ()),   \
+     int, 5)                                                                   \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<const int *> ()),   \
+     int, 5)                                                                   \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool *> ()), int,   \
+     4)                                                                        \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool *> ()), int,   \
+     4)                                                                        \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool *> ()), int,   \
+     4)                                                                        \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<En *> ()), int, 3)  \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<En *> ()), int, 3)  \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<En *> ()), int, 3)  \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), int, 2)     \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), int, 2)     \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), int, 2)     \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T *> ()), float, 1) \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T *> ()), float, 1) \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T *> ()), float, 1) \
+  X (assume_aligned, (std::declval<int *> (), int (), std::declval<T> ()),     \
+     float, 1)                                                                 \
+  X (fpclassify,                                                               \
+     (std::declval<T> (), int (), int (), int (), int (), std::declval<T> ()), \
+     int, 2)                                                                   \
+  X (fpclassify,                                                               \
+     (std::declval<T> (), int (), int (), int (), int (), float ()), float, 1) \
+  X (isgreater, (std::declval<T> (), std::declval<T> ()), int, 1)              \
+  X (isgreaterequal, (std::declval<T> (), std::declval<T> ()), int, 1)         \
+  X (isless, (std::declval<T> (), std::declval<T> ()), int, 1)                 \
+  X (islessequal, (std::declval<T> (), std::declval<T> ()), int, 1)            \
+  X (islessgreater, (std::declval<T> (), std::declval<T> ()), int, 1)          \
+  X (isunordered, (std::declval<T> (), std::declval<T> ()), int, 1)            \
+  X (iseqsig, (std::declval<T> (), std::declval<T> ()), int, 1)                \
+  X (isinf_sign, (std::declval<T> ()), int, 1)                                 \
+  X (isnan, (std::declval<T> ()), int, 1)                                      \
+  X (isnormal, (std::declval<T> ()), int, 1)                                   \
+  X (issignaling, (std::declval<T> ()), int, 1)                                \
+  X (signbit, (std::declval<T> ()), int, 1)                                    \
+  X (isinf, (std::declval<T> ()), int, 1)                                      \
+  X (isfinite, (std::declval<T> ()), int, 1)                                   \
+  X (alloca_with_align, (int (), int (), std::declval<T> ()), BasicClass, 1)   \
+  X (alloca_with_align_and_max, (std::declval<T> (), 1), int, 1)
+
+#define TEMPLATE_DEFS(NAME, PARAMS, TYPE, NUM)                                 \
+  template <typename T, typename = void>                                       \
+  struct is_##NAME##_available_##NUM : std::false_type                         \
+  {                                                                            \
+  };                                                                           \
+  template <typename T>                                                        \
+  struct is_##NAME##_available_##NUM<                                          \
+    T, std::void_t<decltype (__builtin_##NAME PARAMS)>> : std::true_type       \
+  {                                                                            \
+  };
+
+NARGS_CHECKS(TEMPLATE_DEFS)
+
+#define MAKE_ASSERT(NAME, PARAMS, TYPE, NUM) \
+  static_assert(is_##NAME##_available_##NUM<TYPE>::value == false);
+
+void foo() {
+    NARGS_CHECKS(MAKE_ASSERT)
+}
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def
new file mode 100644
index 00000000000..39d9b748d52
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def
@@ -0,0 +1,30 @@
+#include <type_traits>
+
+/* Use std::remove_pointer_t<T> here in order to make `int *` fail when using
+   the INVALID_PARAMETERS block.  Pointers of two different types are allowed
+   in __builtin_speculation_safe_value, it's one argument of a pointer and
+   another argument that is not a pointer that give errors.  */
+#define SPECULATION_SFINAES                                                    \
+  SFINAE_TYPE_CHECK ((std::declval<T> (), std::declval<T> ()),                 \
+		     (std::declval<T> ()),                                     \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T *> ()))
+
+SPECULATION_SFINAES
+
+class X{};
+class Large { public: int arr[10]; };
+class Incomplete;
+
+#define SPECULATION_ASSERTS                                                    \
+  MAKE_SPECULATION_ASSERT (int, true)                                          \
+  MAKE_SPECULATION_ASSERT (float, false)                                       \
+  MAKE_SPECULATION_ASSERT (X, false)                                           \
+  MAKE_SPECULATION_ASSERT (Large, false)                                       \
+  MAKE_SPECULATION_ASSERT (Incomplete, false)                                  \
+  MAKE_SPECULATION_ASSERT (int *, true)                                        \
+  MAKE_SPECULATION_ASSERT (long, true)
+
+int main() {
+    SPECULATION_ASSERTS
+    return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C
new file mode 100644
index 00000000000..bc8f1083a99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C
@@ -0,0 +1,18 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Various types (some that work, some that don't).  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value PARAMS) >> \
+    : std::true_type {};
+
+/* Success according to type of argument.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == SUCCESS);
+
+#include "builtin-speculation-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C
new file mode 100644
index 00000000000..842e349037c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C
@@ -0,0 +1,19 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Invalid parameters with various types (mismatching pointer and non-pointer
+   types).  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value INVALID_PARAMS) >> \
+    : std::true_type {};
+
+/* Mismatching pointer/non-pointer typed parameters always fail.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == false);
+
+#include "builtin-speculation-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C
new file mode 100644
index 00000000000..79956dd972f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C
@@ -0,0 +1,20 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Too many arguments (for any type three arguments should be invalid).  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value \
+			 (int(), int(), std::declval<T>())) >> \
+    : std::true_type {};
+
+/* All types should fail with three arguments.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == false);
+
+#include "builtin-speculation-overloads.def"
+
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C
new file mode 100644
index 00000000000..c024a21fa18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C
@@ -0,0 +1,19 @@
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Optional parameter missing works same as with optional parameter specified.  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value SHORTENED_PARAMS) >> \
+    : std::true_type {};
+
+/* All types should fail with three arguments.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == SUCCESS);
+
+#include "builtin-speculation-overloads.def"
+
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C
new file mode 100644
index 00000000000..8bccafb0e20
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C
@@ -0,0 +1,33 @@
+/* Check that overloaded builtins still error when not in SFINAE context.  */
+// { dg-do compile { target c++17 } }
+#include <type_traits>
+
+/* Checks performed here:
+   Fully specified and invalid function errors before SFINAE happens.  */
+template <typename T, typename = void> struct is_available : std::false_type
+{
+};
+
+/* Should be error here because of the fully specified (and invalid) builtin
+   call. */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } .+5 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } .+4 } */
+/* { dg-error "too few arguments to function" "" { target *-*-* } .+3 } */
+template <typename T>
+struct is_available<
+  T, std::void_t<decltype (__builtin_speculation_safe_value ())>>
+  : std::true_type
+{
+};
+
+/* Should be error here because of the fully specified (and invalid) builtin
+   call. */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } .+5 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } .+4 } */
+/* { dg-error "operand type 'float' is incompatible" "" { target *-*-* } .+3 } */
+template <typename T>
+struct is_available<
+  T, std::void_t<decltype (__builtin_speculation_safe_value (float()))>>
+  : std::true_type
+{
+};
diff --git a/gcc/testsuite/g++.dg/template/builtin-validate-nargs.C b/gcc/testsuite/g++.dg/template/builtin-validate-nargs.C
new file mode 100644
index 00000000000..c6a7aec48d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-validate-nargs.C
@@ -0,0 +1,67 @@
+/* Testing that we avoid error in SFINAE context when number of arguments are
+   invalid in a builtin template argument.  */
+// { dg-do compile { target c++17 } }
+/* { dg-additional-options "-Wno-macro-redefined" { target { *-*-* } } } */
+#include <type_traits>
+
+/* Here checking the builtin_function_validate_nargs function doesn't error
+   in an SFINAE context.  */
+
+template <typename T, typename = void> struct is_available : std::false_type
+{
+};
+
+/* Make one testcase for each of the functions in
+   check_builtin_function_arguments that uses builtin_function_validate_nargs.
+ */
+#define NARGS_CHECKS(X)                                                        \
+  X (constant_p, (std::declval<T> (), std::declval<T> ()))                     \
+  X (isfinite, (std::declval<T> (), std::declval<T> ()))                       \
+  X (isinf, (std::declval<T> (), std::declval<T> ()))                          \
+  X (isinf_sign, (std::declval<T> (), std::declval<T> ()))                     \
+  X (isnan, (std::declval<T> (), std::declval<T> ()))                          \
+  X (isnormal, (std::declval<T> (), std::declval<T> ()))                       \
+  X (issignaling, (std::declval<T> (), std::declval<T> ()))                    \
+  X (signbit, (std::declval<T> (), std::declval<T> ()))                        \
+  X (isgreater, (std::declval<T> ()))                                          \
+  X (isgreaterequal, (std::declval<T> ()))                                     \
+  X (isless, (std::declval<T> ()))                                             \
+  X (islessequal, (std::declval<T> ()))                                        \
+  X (islessgreater, (std::declval<T> ()))                                      \
+  X (isunordered, (std::declval<T> ()))                                        \
+  X (iseqsig, (std::declval<T> ()))                                            \
+  X (fpclassify, (std::declval<T> ()))                                         \
+  X (assume_aligned, (std::declval<T> ()))                                     \
+  X (add_overflow, (std::declval<T> ()))                                       \
+  X (sub_overflow, (std::declval<T> ()))                                       \
+  X (mul_overflow, (std::declval<T> ()))                                       \
+  X (add_overflow_p, (std::declval<T> ()))                                     \
+  X (sub_overflow_p, (std::declval<T> ()))                                     \
+  X (mul_overflow_p, (std::declval<T> ()))                                     \
+  X (clear_padding, (std::declval<T> (), std::declval<T> ()))                  \
+  X (clzg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))       \
+  X (ctzg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))       \
+  X (clrsbg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))     \
+  X (ffsg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))       \
+  X (parityg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))    \
+  X (popcountg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))
+
+#define TEMPLATE_DEFS(NAME, PARAMS)                                            \
+  template <typename T, typename = void>                                       \
+  struct is_##NAME##_available : std::false_type                               \
+  {                                                                            \
+  };                                                                           \
+  template <typename T>                                                        \
+  struct is_##NAME##_available<                                                \
+    T, std::void_t<decltype (__builtin_##NAME PARAMS)>> : std::true_type       \
+  {                                                                            \
+  };
+
+NARGS_CHECKS(TEMPLATE_DEFS)
+
+#define MAKE_ASSERT(NAME, PARAMS) \
+  static_assert(is_##NAME##_available<int>::value == false);
+
+void foo() {
+    NARGS_CHECKS(MAKE_ASSERT)
+}
diff mbox series

Patch

diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index ec6a5da892d..90550dfbeac 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -6313,16 +6313,18 @@  check_function_arguments_recurse (void (*callback)
 
 static bool
 builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs,
-				 int required)
+				 int required, bool complain)
 {
   if (nargs < required)
     {
-      error_at (loc, "too few arguments to function %qE", fndecl);
+      if (complain)
+	error_at (loc, "too few arguments to function %qE", fndecl);
       return false;
     }
   else if (nargs > required)
     {
-      error_at (loc, "too many arguments to function %qE", fndecl);
+      if (complain)
+	error_at (loc, "too many arguments to function %qE", fndecl);
       return false;
     }
   return true;
@@ -6343,16 +6345,16 @@  builtin_function_validate_nargs (location_t loc, tree fndecl, int nargs,
 
 bool
 check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
-				  tree fndecl, tree orig_fndecl,
-				  int nargs, tree *args)
+				  tree fndecl, tree orig_fndecl, int nargs,
+				  tree *args, bool complain)
 {
   if (!fndecl_built_in_p (fndecl))
     return true;
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
     return (!targetm.check_builtin_call
-	    || targetm.check_builtin_call (loc, arg_loc, fndecl,
-					   orig_fndecl, nargs, args));
+	    || targetm.check_builtin_call (loc, arg_loc, fndecl, orig_fndecl,
+					   nargs, args, complain));
 
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_FRONTEND)
     return true;
@@ -6363,9 +6365,11 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX:
       if (!tree_fits_uhwi_p (args[2]))
 	{
-	  error_at (ARG_LOCATION (2),
-		    "third argument to function %qE must be a constant integer",
-		    fndecl);
+	  if (complain)
+	    error_at (
+	      ARG_LOCATION (2),
+	      "third argument to function %qE must be a constant integer",
+	      fndecl);
 	  return false;
 	}
       /* fall through */
@@ -6388,17 +6392,18 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 	/* Reject invalid alignments.  */
 	if (align < BITS_PER_UNIT || maxalign < align)
 	  {
-	    error_at (ARG_LOCATION (1),
-		      "second argument to function %qE must be a constant "
-		      "integer power of 2 between %qi and %qu bits",
-		      fndecl, BITS_PER_UNIT, maxalign);
+	    if (complain)
+	      error_at (ARG_LOCATION (1),
+			"second argument to function %qE must be a constant "
+			"integer power of 2 between %qi and %qu bits",
+			fndecl, BITS_PER_UNIT, maxalign);
 	    return false;
 	  }
 	return true;
       }
 
     case BUILT_IN_CONSTANT_P:
-      return builtin_function_validate_nargs (loc, fndecl, nargs, 1);
+      return builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain);
 
     case BUILT_IN_ISFINITE:
     case BUILT_IN_ISINF:
@@ -6407,12 +6412,15 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ISNORMAL:
     case BUILT_IN_ISSIGNALING:
     case BUILT_IN_SIGNBIT:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain))
 	{
 	  if (TREE_CODE (TREE_TYPE (args[0])) != REAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (0), "non-floating-point argument in "
-			"call to function %qE", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "non-floating-point argument in "
+			  "call to function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6426,7 +6434,7 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ISLESSGREATER:
     case BUILT_IN_ISUNORDERED:
     case BUILT_IN_ISEQSIG:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2, complain))
 	{
 	  enum tree_code code0, code1;
 	  code0 = TREE_CODE (TREE_TYPE (args[0]));
@@ -6437,8 +6445,11 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 		|| ((code0 == INTEGER_TYPE || code0 == BITINT_TYPE)
 		    && code1 == REAL_TYPE)))
 	    {
-	      error_at (loc, "non-floating-point arguments in call to "
-			"function %qE", fndecl);
+	      if (complain)
+		error_at (loc,
+			  "non-floating-point arguments in call to "
+			  "function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6446,20 +6457,26 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
       return false;
 
     case BUILT_IN_FPCLASSIFY:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 6))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 6, complain))
 	{
 	  for (unsigned int i = 0; i < 5; i++)
 	    if (TREE_CODE (args[i]) != INTEGER_CST)
 	      {
-		error_at (ARG_LOCATION (i), "non-const integer argument %u in "
-			  "call to function %qE", i + 1, fndecl);
+		if (complain)
+		  error_at (ARG_LOCATION (i),
+			    "non-const integer argument %u in "
+			    "call to function %qE",
+			    i + 1, fndecl);
 		return false;
 	      }
 
 	  if (TREE_CODE (TREE_TYPE (args[5])) != REAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (5), "non-floating-point argument in "
-			"call to function %qE", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (5),
+			  "non-floating-point argument in "
+			  "call to function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6467,14 +6484,18 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
       return false;
 
     case BUILT_IN_ASSUME_ALIGNED:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2 + (nargs > 2)))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 2 + (nargs > 2),
+					   complain))
 	{
 	  if (nargs >= 3
 	      && TREE_CODE (TREE_TYPE (args[2])) != INTEGER_TYPE
 	      && TREE_CODE (TREE_TYPE (args[2])) != BITINT_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "non-integer argument 3 in call to "
-			"function %qE", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "non-integer argument 3 in call to "
+			  "function %qE",
+			  fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6484,47 +6505,63 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_MUL_OVERFLOW:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3, complain))
 	{
 	  unsigned i;
 	  for (i = 0; i < 2; i++)
 	    if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
 	      {
-		error_at (ARG_LOCATION (i), "argument %u in call to function "
-			  "%qE does not have integral type", i + 1, fndecl);
+		if (complain)
+		  error_at (ARG_LOCATION (i),
+			    "argument %u in call to function "
+			    "%qE does not have integral type",
+			    i + 1, fndecl);
 		return false;
 	      }
 	  if (TREE_CODE (TREE_TYPE (args[2])) != POINTER_TYPE
 	      || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (args[2]))))
 	    {
-	      error_at (ARG_LOCATION (2), "argument 3 in call to function %qE "
-			"does not have pointer to integral type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument 3 in call to function %qE "
+			  "does not have pointer to integral type",
+			  fndecl);
 	      return false;
 	    }
 	  else if (TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) == ENUMERAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument 3 in call to function %qE "
-			"has pointer to enumerated type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument 3 in call to function %qE "
+			  "has pointer to enumerated type",
+			  fndecl);
 	      return false;
 	    }
 	  else if (TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) == BOOLEAN_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument 3 in call to function %qE "
-			"has pointer to boolean type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument 3 in call to function %qE "
+			  "has pointer to boolean type",
+			  fndecl);
 	      return false;
 	    }
 	  else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[2]))))
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 3, fndecl, "const",
-			TREE_TYPE (args[2]));
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  3, fndecl, "const", TREE_TYPE (args[2]));
 	      return false;
 	    }
 	  else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[2]))))
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 3, fndecl,
-			"_Atomic", TREE_TYPE (args[2]));
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  3, fndecl, "_Atomic", TREE_TYPE (args[2]));
 	      return false;
 	    }
 	  return true;
@@ -6534,26 +6571,35 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
     case BUILT_IN_ADD_OVERFLOW_P:
     case BUILT_IN_SUB_OVERFLOW_P:
     case BUILT_IN_MUL_OVERFLOW_P:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3, complain))
 	{
 	  unsigned i;
 	  for (i = 0; i < 3; i++)
 	    if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
 	      {
-		error_at (ARG_LOCATION (i), "argument %u in call to function "
-			  "%qE does not have integral type", i + 1, fndecl);
+		if (complain)
+		  error_at (ARG_LOCATION (i),
+			    "argument %u in call to function "
+			    "%qE does not have integral type",
+			    i + 1, fndecl);
 		return false;
 	      }
 	  if (TREE_CODE (TREE_TYPE (args[2])) == ENUMERAL_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function "
-			"%qE has enumerated type", 3, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function "
+			  "%qE has enumerated type",
+			  3, fndecl);
 	      return false;
 	    }
 	  else if (TREE_CODE (TREE_TYPE (args[2])) == BOOLEAN_TYPE)
 	    {
-	      error_at (ARG_LOCATION (2), "argument %u in call to function "
-			"%qE has boolean type", 3, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (2),
+			  "argument %u in call to function "
+			  "%qE has boolean type",
+			  3, fndecl);
 	      return false;
 	    }
 	  return true;
@@ -6561,32 +6607,42 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
       return false;
 
     case BUILT_IN_CLEAR_PADDING:
-      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 1, complain))
 	{
 	  if (!POINTER_TYPE_P (TREE_TYPE (args[0])))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function "
-			"%qE does not have pointer type", 1, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function "
+			  "%qE does not have pointer type",
+			  1, fndecl);
 	      return false;
 	    }
 	  else if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (args[0]))))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function "
-			"%qE points to incomplete type", 1, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function "
+			  "%qE points to incomplete type",
+			  1, fndecl);
 	      return false;
 	    }
 	  else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (args[0]))))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 1, fndecl, "const",
-			TREE_TYPE (args[0]));
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  1, fndecl, "const", TREE_TYPE (args[0]));
 	      return false;
 	    }
 	  else if (TYPE_ATOMIC (TREE_TYPE (TREE_TYPE (args[0]))))
 	    {
-	      error_at (ARG_LOCATION (0), "argument %u in call to function %qE "
-			"has pointer to %qs type (%qT)", 1, fndecl,
-			"_Atomic", TREE_TYPE (args[0]));
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument %u in call to function %qE "
+			  "has pointer to %qs type (%qT)",
+			  1, fndecl, "_Atomic", TREE_TYPE (args[0]));
 	      return false;
 	    }
 	  return true;
@@ -6605,8 +6661,11 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 	{
 	  if (!INTEGRAL_TYPE_P (TREE_TYPE (args[1])))
 	    {
-	      error_at (ARG_LOCATION (1), "argument %u in call to function "
-			"%qE does not have integral type", 2, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (1),
+			  "argument %u in call to function "
+			  "%qE does not have integral type",
+			  2, fndecl);
 	      return false;
 	    }
 	  if ((TYPE_PRECISION (TREE_TYPE (args[1]))
@@ -6615,30 +6674,43 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 		  == TYPE_PRECISION (integer_type_node)
 		  && TYPE_UNSIGNED (TREE_TYPE (args[1]))))
 	    {
-	      error_at (ARG_LOCATION (1), "argument %u in call to function "
-			"%qE does not have %<int%> type", 2, fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (1),
+			  "argument %u in call to function "
+			  "%qE does not have %<int%> type",
+			  2, fndecl);
 	      return false;
 	    }
 	}
-      else if (!builtin_function_validate_nargs (loc, fndecl, nargs, 1))
+      else if (!builtin_function_validate_nargs (loc, fndecl, nargs, 1,
+						 complain))
 	return false;
 
       if (!INTEGRAL_TYPE_P (TREE_TYPE (args[0])))
 	{
-	  error_at (ARG_LOCATION (0), "argument %u in call to function "
-		    "%qE does not have integral type", 1, fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument %u in call to function "
+		      "%qE does not have integral type",
+		      1, fndecl);
 	  return false;
 	}
       if (TREE_CODE (TREE_TYPE (args[0])) == ENUMERAL_TYPE)
 	{
-	  error_at (ARG_LOCATION (0), "argument %u in call to function "
-		    "%qE has enumerated type", 1, fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument %u in call to function "
+		      "%qE has enumerated type",
+		      1, fndecl);
 	  return false;
 	}
       if (TREE_CODE (TREE_TYPE (args[0])) == BOOLEAN_TYPE)
 	{
-	  error_at (ARG_LOCATION (0), "argument %u in call to function "
-		    "%qE has boolean type", 1, fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument %u in call to function "
+		      "%qE has boolean type",
+		      1, fndecl);
 	  return false;
 	}
       if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_FFSG
@@ -6646,15 +6718,21 @@  check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
 	{
 	  if (TYPE_UNSIGNED (TREE_TYPE (args[0])))
 	    {
-	      error_at (ARG_LOCATION (0), "argument 1 in call to function "
-			"%qE has unsigned type", fndecl);
+	      if (complain)
+		error_at (ARG_LOCATION (0),
+			  "argument 1 in call to function "
+			  "%qE has unsigned type",
+			  fndecl);
 	      return false;
 	    }
 	}
       else if (!TYPE_UNSIGNED (TREE_TYPE (args[0])))
 	{
-	  error_at (ARG_LOCATION (0), "argument 1 in call to function "
-		    "%qE has signed type", fndecl);
+	  if (complain)
+	    error_at (ARG_LOCATION (0),
+		      "argument 1 in call to function "
+		      "%qE has signed type",
+		      fndecl);
 	  return false;
 	}
       return true;
@@ -7235,7 +7313,8 @@  builtin_type_for_size (int size, bool unsignedp)
    the size is too large; 0 if the argument type is a pointer or the
    size if it is integral.  */
 static enum built_in_function
-speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
+speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params,
+				     bool complain)
 {
   /* Type of the argument.  */
   tree type;
@@ -7243,7 +7322,8 @@  speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
 
   if (vec_safe_is_empty (params))
     {
-      error ("too few arguments to function %qE", function);
+      if (complain)
+	error ("too few arguments to function %qE", function);
       return BUILT_IN_NONE;
     }
 
@@ -7272,7 +7352,7 @@  speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
  incompatible:
   /* Issue the diagnostic only if the argument is valid, otherwise
      it would be redundant at best and could be misleading.  */
-  if (type != error_mark_node)
+  if (type != error_mark_node && complain)
     error ("operand type %qT is incompatible with argument %d of %qE",
 	   type, 1, function);
 
@@ -7284,19 +7364,21 @@  speculation_safe_value_resolve_call (tree function, vec<tree, va_gc> *params)
    argument, if present, must be type compatible with the first.  */
 static bool
 speculation_safe_value_resolve_params (location_t loc, tree orig_function,
-				       vec<tree, va_gc> *params)
+				       vec<tree, va_gc> *params, bool complain)
 {
   tree val;
 
   if (params->length () == 0)
     {
-      error_at (loc, "too few arguments to function %qE", orig_function);
+      if (complain)
+	error_at (loc, "too few arguments to function %qE", orig_function);
       return false;
     }
 
   else if (params->length () > 2)
     {
-      error_at (loc, "too many arguments to function %qE", orig_function);
+      if (complain)
+	error_at (loc, "too many arguments to function %qE", orig_function);
       return false;
     }
 
@@ -7306,9 +7388,9 @@  speculation_safe_value_resolve_params (location_t loc, tree orig_function,
   if (!(TREE_CODE (TREE_TYPE (val)) == POINTER_TYPE
 	|| TREE_CODE (TREE_TYPE (val)) == INTEGER_TYPE))
     {
-      error_at (loc,
-		"expecting argument of type pointer or of type integer "
-		"for argument 1");
+      if (complain)
+	error_at (loc, "expecting argument of type pointer or of type integer "
+		       "for argument 1");
       return false;
     }
   (*params)[0] = val;
@@ -7323,7 +7405,8 @@  speculation_safe_value_resolve_params (location_t loc, tree orig_function,
       if (!(TREE_TYPE (val) == TREE_TYPE (val2)
 	    || useless_type_conversion_p (TREE_TYPE (val), TREE_TYPE (val2))))
 	{
-	  error_at (loc, "both arguments must be compatible");
+	  if (complain)
+	    error_at (loc, "both arguments must be compatible");
 	  return false;
 	}
       (*params)[1] = val2;
@@ -7359,7 +7442,7 @@  speculation_safe_value_resolve_return (tree first_param, tree result)
 
 static int
 sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
-		   bool orig_format)
+		   bool orig_format, bool complain)
 {
   /* Type of the argument.  */
   tree argtype;
@@ -7369,7 +7452,8 @@  sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
 
   if (vec_safe_is_empty (params))
     {
-      error ("too few arguments to function %qE", function);
+      if (complain)
+	error ("too few arguments to function %qE", function);
       return 0;
     }
 
@@ -7410,7 +7494,7 @@  sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
  incompatible:
   /* Issue the diagnostic only if the argument is valid, otherwise
      it would be redundant at best and could be misleading.  */
-  if (argtype != error_mark_node)
+  if (argtype != error_mark_node && complain)
     error ("operand type %qT is incompatible with argument %d of %qE",
 	   argtype, 1, function);
   return 0;
@@ -7423,7 +7507,7 @@  sync_resolve_size (tree function, vec<tree, va_gc> *params, bool fetch,
 
 static bool
 sync_resolve_params (location_t loc, tree orig_function, tree function,
-		     vec<tree, va_gc> *params, bool orig_format)
+		     vec<tree, va_gc> *params, bool orig_format, bool complain)
 {
   function_args_iterator iter;
   tree ptype;
@@ -7452,7 +7536,8 @@  sync_resolve_params (location_t loc, tree orig_function, tree function,
       ++parmnum;
       if (params->length () <= parmnum)
 	{
-	  error_at (loc, "too few arguments to function %qE", orig_function);
+	  if (complain)
+	    error_at (loc, "too few arguments to function %qE", orig_function);
 	  return false;
 	}
 
@@ -7478,7 +7563,8 @@  sync_resolve_params (location_t loc, tree orig_function, tree function,
   /* __atomic routines are not variadic.  */
   if (!orig_format && params->length () != parmnum + 1)
     {
-      error_at (loc, "too many arguments to function %qE", orig_function);
+      if (complain)
+	error_at (loc, "too many arguments to function %qE", orig_function);
       return false;
     }
 
@@ -7515,7 +7601,7 @@  sync_resolve_return (tree first_param, tree result, bool orig_format)
 
 static int
 get_atomic_generic_size (location_t loc, tree function,
-			 vec<tree, va_gc> *params)
+			 vec<tree, va_gc> *params, bool complain)
 {
   unsigned int n_param;
   unsigned int n_model;
@@ -7553,7 +7639,9 @@  get_atomic_generic_size (location_t loc, tree function,
 
   if (vec_safe_length (params) != n_param)
     {
-      error_at (loc, "incorrect number of arguments to function %qE", function);
+      if (complain)
+	error_at (loc, "incorrect number of arguments to function %qE",
+		  function);
       return 0;
     }
 
@@ -7567,24 +7655,27 @@  get_atomic_generic_size (location_t loc, tree function,
     }
   if (TREE_CODE (type_0) != POINTER_TYPE || VOID_TYPE_P (TREE_TYPE (type_0)))
     {
-      error_at (loc, "argument 1 of %qE must be a non-void pointer type",
-		function);
+      if (complain)
+	error_at (loc, "argument 1 of %qE must be a non-void pointer type",
+		  function);
       return 0;
     }
 
   if (!COMPLETE_TYPE_P (TREE_TYPE (type_0)))
     {
-      error_at (loc, "argument 1 of %qE must be a pointer to a complete type",
-		function);
+      if (complain)
+	error_at (loc, "argument 1 of %qE must be a pointer to a complete type",
+		  function);
       return 0;
     }
 
   /* Types must be compile time constant sizes. */
   if (!tree_fits_uhwi_p ((TYPE_SIZE_UNIT (TREE_TYPE (type_0)))))
     {
-      error_at (loc, 
-		"argument 1 of %qE must be a pointer to a constant size type",
-		function);
+      if (complain)
+	error_at (loc,
+		  "argument 1 of %qE must be a pointer to a constant size type",
+		  function);
       return 0;
     }
 
@@ -7593,9 +7684,10 @@  get_atomic_generic_size (location_t loc, tree function,
   /* Zero size objects are not allowed.  */
   if (size_0 == 0)
     {
-      error_at (loc, 
-		"argument 1 of %qE must be a pointer to a nonzero size object",
-		function);
+      if (complain)
+	error_at (
+	  loc, "argument 1 of %qE must be a pointer to a nonzero size object",
+	  function);
       return 0;
     }
 
@@ -7615,30 +7707,38 @@  get_atomic_generic_size (location_t loc, tree function,
 	}
       if (!POINTER_TYPE_P (type))
 	{
-	  error_at (loc, "argument %d of %qE must be a pointer type", x + 1,
-		    function);
+	  if (complain)
+	    error_at (loc, "argument %d of %qE must be a pointer type", x + 1,
+		      function);
 	  return 0;
 	}
       else if (TYPE_SIZE_UNIT (TREE_TYPE (type))
 	       && TREE_CODE ((TYPE_SIZE_UNIT (TREE_TYPE (type))))
 		  != INTEGER_CST)
 	{
-	  error_at (loc, "argument %d of %qE must be a pointer to a constant "
-		    "size type", x + 1, function);
+	  if (complain)
+	    error_at (loc,
+		      "argument %d of %qE must be a pointer to a constant "
+		      "size type",
+		      x + 1, function);
 	  return 0;
 	}
       else if (FUNCTION_POINTER_TYPE_P (type))
 	{
-	  error_at (loc, "argument %d of %qE must not be a pointer to a "
-		    "function", x + 1, function);
+	  if (complain)
+	    error_at (loc,
+		      "argument %d of %qE must not be a pointer to a "
+		      "function",
+		      x + 1, function);
 	  return 0;
 	}
       tree type_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
       size = type_size ? tree_to_uhwi (type_size) : 0;
       if (size != size_0)
 	{
-	  error_at (loc, "size mismatch in argument %d of %qE", x + 1,
-		    function);
+	  if (complain)
+	    error_at (loc, "size mismatch in argument %d of %qE", x + 1,
+		      function);
 	  return 0;
 	}
 
@@ -7650,8 +7750,11 @@  get_atomic_generic_size (location_t loc, tree function,
 	  {
 	    if (c_dialect_cxx ())
 	      {
-		error_at (loc, "argument %d of %qE must not be a pointer to "
-			  "a %<const%> type", x + 1, function);
+		if (complain)
+		  error_at (loc,
+			    "argument %d of %qE must not be a pointer to "
+			    "a %<const%> type",
+			    x + 1, function);
 		return 0;
 	      }
 	    else
@@ -7664,8 +7767,11 @@  get_atomic_generic_size (location_t loc, tree function,
 	  {
 	    if (c_dialect_cxx ())
 	      {
-		error_at (loc, "argument %d of %qE must not be a pointer to "
-			  "a %<volatile%> type", x + 1, function);
+		if (complain)
+		  error_at (loc,
+			    "argument %d of %qE must not be a pointer to "
+			    "a %<volatile%> type",
+			    x + 1, function);
 		return 0;
 	      }
 	    else
@@ -7682,8 +7788,9 @@  get_atomic_generic_size (location_t loc, tree function,
       tree p = (*params)[x];
       if (!INTEGRAL_TYPE_P (TREE_TYPE (p)))
 	{
-	  error_at (loc, "non-integer memory model argument %d of %qE", x + 1,
-		    function);
+	  if (complain)
+	    error_at (loc, "non-integer memory model argument %d of %qE", x + 1,
+		      function);
 	  return 0;
 	}
       p = fold_for_warn (p);
@@ -7773,12 +7880,13 @@  atomic_size_supported_p (int n)
    FALSE is returned if processing for the _N variation is required, and 
    NEW_RETURN is set to the return value the result is copied into.  */
 static bool
-resolve_overloaded_atomic_exchange (location_t loc, tree function, 
-				    vec<tree, va_gc> *params, tree *new_return)
+resolve_overloaded_atomic_exchange (location_t loc, tree function,
+				    vec<tree, va_gc> *params, tree *new_return,
+				    bool complain)
 {	
   tree p0, p1, p2, p3;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -7842,13 +7950,13 @@  resolve_overloaded_atomic_exchange (location_t loc, tree function,
    FALSE is returned if processing for the _N variation is required.  */
 
 static bool
-resolve_overloaded_atomic_compare_exchange (location_t loc, tree function, 
-					    vec<tree, va_gc> *params, 
-					    tree *new_return)
+resolve_overloaded_atomic_compare_exchange (location_t loc, tree function,
+					    vec<tree, va_gc> *params,
+					    tree *new_return, bool complain)
 {	
   tree p0, p1, p2;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -7924,12 +8032,13 @@  resolve_overloaded_atomic_compare_exchange (location_t loc, tree function,
    NEW_RETURN is set to the return value the result is copied into.  */
 
 static bool
-resolve_overloaded_atomic_load (location_t loc, tree function, 
-				vec<tree, va_gc> *params, tree *new_return)
+resolve_overloaded_atomic_load (location_t loc, tree function,
+				vec<tree, va_gc> *params, tree *new_return,
+				bool complain)
 {	
   tree p0, p1, p2;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -7984,12 +8093,13 @@  resolve_overloaded_atomic_load (location_t loc, tree function,
    NEW_RETURN is set to the return value the result is copied into.  */
 
 static bool
-resolve_overloaded_atomic_store (location_t loc, tree function, 
-				 vec<tree, va_gc> *params, tree *new_return)
+resolve_overloaded_atomic_store (location_t loc, tree function,
+				 vec<tree, va_gc> *params, tree *new_return,
+				 bool complain)
 {	
   tree p0, p1;
   tree I_type, I_type_ptr;
-  int n = get_atomic_generic_size (loc, function, params);
+  int n = get_atomic_generic_size (loc, function, params, complain);
 
   /* Size of 0 is an error condition.  */
   if (n == 0)
@@ -8269,7 +8379,7 @@  atomic_bitint_fetch_using_cas_loop (location_t loc,
 
 tree
 resolve_overloaded_builtin (location_t loc, tree function,
-			    vec<tree, va_gc> *params)
+			    vec<tree, va_gc> *params, bool complain)
 {
   /* Is function one of the _FETCH_OP_ or _OP_FETCH_ built-ins?
      Those are not valid to call with a pointer to _Bool (or C++ bool)
@@ -8284,7 +8394,8 @@  resolve_overloaded_builtin (location_t loc, tree function,
       break;
     case BUILT_IN_MD:
       if (targetm.resolve_overloaded_builtin)
-	return targetm.resolve_overloaded_builtin (loc, function, params);
+	return targetm.resolve_overloaded_builtin (loc, function, params,
+						   complain);
       else
 	return NULL_TREE;
     default:
@@ -8299,13 +8410,14 @@  resolve_overloaded_builtin (location_t loc, tree function,
       {
 	tree new_function, first_param, result;
 	enum built_in_function fncode
-	  = speculation_safe_value_resolve_call (function, params);
+	  = speculation_safe_value_resolve_call (function, params, complain);
 
 	if (fncode == BUILT_IN_NONE)
 	  return error_mark_node;
 
 	first_param = (*params)[0];
-	if (!speculation_safe_value_resolve_params (loc, function, params))
+	if (!speculation_safe_value_resolve_params (loc, function, params,
+						    complain))
 	  return error_mark_node;
 
 	if (targetm.have_speculation_safe_value (true))
@@ -8324,6 +8436,11 @@  resolve_overloaded_builtin (location_t loc, tree function,
 	    /* This target doesn't have, or doesn't need, active mitigation
 	       against incorrect speculative execution.  Simply return the
 	       first parameter to the builtin.  */
+	    /* N.b. emit the warning whether or not we're given
+	       `complain`, since that argument is for things like
+	       SFINAE whereas the situation we're warning on could not be
+	       picked up by that (because we're not returning
+	       `error_mark_node`).  */
 	    if (!targetm.have_speculation_safe_value (false))
 	      /* The user has invoked __builtin_speculation_safe_value
 		 even though __HAVE_SPECULATION_SAFE_VALUE is not
@@ -8356,41 +8473,41 @@  resolve_overloaded_builtin (location_t loc, tree function,
 	  {
 	  case BUILT_IN_ATOMIC_EXCHANGE:
 	    {
-	      if (resolve_overloaded_atomic_exchange (loc, function, params,
-						      &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_EXCHANGE_N;
-	      break;
+	    if (resolve_overloaded_atomic_exchange (loc, function, params,
+						    &new_return, complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_EXCHANGE_N;
+	    break;
 	    }
 
 	  case BUILT_IN_ATOMIC_COMPARE_EXCHANGE:
 	    {
-	      if (resolve_overloaded_atomic_compare_exchange (loc, function,
-							      params,
-							      &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_COMPARE_EXCHANGE_N;
-	      break;
+	    if (resolve_overloaded_atomic_compare_exchange (loc, function,
+							    params, &new_return,
+							    complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_COMPARE_EXCHANGE_N;
+	    break;
 	    }
 	  case BUILT_IN_ATOMIC_LOAD:
 	    {
-	      if (resolve_overloaded_atomic_load (loc, function, params,
-						  &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_LOAD_N;
-	      break;
+	    if (resolve_overloaded_atomic_load (loc, function, params,
+						&new_return, complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_LOAD_N;
+	    break;
 	    }
 	  case BUILT_IN_ATOMIC_STORE:
 	    {
-	      if (resolve_overloaded_atomic_store (loc, function, params,
-						   &new_return))
-		return new_return;
-	      /* Change to the _N variant.  */
-	      orig_code = BUILT_IN_ATOMIC_STORE_N;
-	      break;
+	    if (resolve_overloaded_atomic_store (loc, function, params,
+						 &new_return, complain))
+	      return new_return;
+	    /* Change to the _N variant.  */
+	    orig_code = BUILT_IN_ATOMIC_STORE_N;
+	    break;
 	    }
 	  default:
 	    gcc_unreachable ();
@@ -8442,7 +8559,8 @@  resolve_overloaded_builtin (location_t loc, tree function,
 		      && orig_code != BUILT_IN_SYNC_LOCK_TEST_AND_SET_N
 		      && orig_code != BUILT_IN_SYNC_LOCK_RELEASE_N);
 
-	int n = sync_resolve_size (function, params, fetch_op, orig_format);
+	int n = sync_resolve_size (function, params, fetch_op, orig_format,
+				   complain);
 	tree new_function, first_param, result;
 	enum built_in_function fncode;
 
@@ -8450,13 +8568,24 @@  resolve_overloaded_builtin (location_t loc, tree function,
 	  return error_mark_node;
 
 	if (n == -1)
-	  return atomic_bitint_fetch_using_cas_loop (loc, orig_code,
-						     function, params);
+	  {
+	    /* complain is related to SFINAE context.
+	       _BitInt is not defined in C++, hence can't enter this clause
+	       with complain unset.  Even if at the abstraction level
+	       this complain is unset that still makes sense (whether
+	       this function should report an error or not if anything is
+	       wrong).
+	       Since can't test avoiding an error when this value is false not
+	       writing the code and instead asserting value is not set.  */
+	    gcc_assert (complain);
+	    return atomic_bitint_fetch_using_cas_loop (loc, orig_code, function,
+						       params);
+	  }
 
 	fncode = (enum built_in_function)((int)orig_code + exact_log2 (n) + 1);
 	new_function = builtin_decl_explicit (fncode);
 	if (!sync_resolve_params (loc, function, new_function, params,
-				  orig_format))
+				  orig_format, complain))
 	  return error_mark_node;
 
 	first_param = (*params)[0];
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 027f077d51b..d2bbe9d4c20 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -857,8 +857,9 @@  extern void check_function_arguments_recurse (void (*)
 					      void *, tree,
 					      unsigned HOST_WIDE_INT,
 					      opt_code);
-extern bool check_builtin_function_arguments (location_t, vec<location_t>,
-					      tree, tree, int, tree *);
+extern bool
+check_builtin_function_arguments (location_t, vec<location_t>, tree, tree, int,
+				  tree *, bool = true);
 extern void check_function_format (const_tree, tree, int, tree *,
 				   vec<location_t> *,
 				   bool (*comp_types) (tree, tree));
@@ -1103,7 +1104,8 @@  extern tree build_function_call_vec (location_t, vec<location_t>, tree,
 				     vec<tree, va_gc> *, vec<tree, va_gc> *,
 				     tree = NULL_TREE);
 
-extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *);
+extern tree
+resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *, bool = true);
 
 extern tree finish_label_address_expr (tree, location_t);
 
diff --git a/gcc/config/aarch64/aarch64-c.cc b/gcc/config/aarch64/aarch64-c.cc
index f9b9e379375..ddf5a6c453c 100644
--- a/gcc/config/aarch64/aarch64-c.cc
+++ b/gcc/config/aarch64/aarch64-c.cc
@@ -365,8 +365,8 @@  aarch64_pragma_aarch64 (cpp_reader *)
 
 /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN.  */
 static tree
-aarch64_resolve_overloaded_builtin (unsigned int uncast_location,
-				    tree fndecl, void *uncast_arglist)
+aarch64_resolve_overloaded_builtin (unsigned int uncast_location, tree fndecl,
+				    void *uncast_arglist, bool)
 {
   vec<tree, va_gc> empty = {};
   location_t location = (location_t) uncast_location;
@@ -396,7 +396,7 @@  aarch64_resolve_overloaded_builtin (unsigned int uncast_location,
 static bool
 aarch64_check_builtin_call (location_t loc, vec<location_t> arg_loc,
 			    tree fndecl, tree orig_fndecl,
-			    unsigned int nargs, tree *args)
+			    unsigned int nargs, tree *args, bool)
 {
   unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
   unsigned int subcode = code >> AARCH64_BUILTIN_SHIFT;
diff --git a/gcc/config/arm/arm-builtins.cc b/gcc/config/arm/arm-builtins.cc
index 74cea8900b4..ee0f1c551ca 100644
--- a/gcc/config/arm/arm-builtins.cc
+++ b/gcc/config/arm/arm-builtins.cc
@@ -4211,7 +4211,7 @@  arm_general_check_builtin_call (unsigned int code)
 bool
 arm_check_builtin_call (location_t loc, vec<location_t> arg_loc,
 			tree fndecl, tree orig_fndecl,
-			unsigned int nargs, tree *args)
+			unsigned int nargs, tree *args, bool)
 {
   unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
   unsigned int subcode = code >> ARM_BUILTIN_SHIFT;
diff --git a/gcc/config/arm/arm-c.cc b/gcc/config/arm/arm-c.cc
index 6e10262b067..74006752aff 100644
--- a/gcc/config/arm/arm-c.cc
+++ b/gcc/config/arm/arm-c.cc
@@ -163,7 +163,7 @@  arm_pragma_arm (cpp_reader *)
 /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN.  */
 tree
 arm_resolve_overloaded_builtin (location_t loc, tree fndecl,
-				void *uncast_arglist)
+				void *uncast_arglist, bool)
 {
   enum resolver_ident resolver = arm_describe_resolver (fndecl);
   if (resolver == arm_cde_resolver)
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index b694589cab4..7f0f20542d6 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -32,7 +32,7 @@  extern int use_return_insn (int, rtx);
 extern bool use_simple_return_p (void);
 extern enum reg_class arm_regno_class (int);
 extern bool arm_check_builtin_call (location_t , vec<location_t> , tree,
-				    tree, unsigned int, tree *);
+				    tree, unsigned int, tree *, bool);
 extern void arm_load_pic_register (unsigned long, rtx);
 extern int arm_volatile_func (void);
 extern void arm_expand_prologue (void);
diff --git a/gcc/config/avr/avr-c.cc b/gcc/config/avr/avr-c.cc
index 81c91c38363..34531b40a9d 100644
--- a/gcc/config/avr/avr-c.cc
+++ b/gcc/config/avr/avr-c.cc
@@ -48,7 +48,8 @@  enum avr_builtin_id
 /* Implement `TARGET_RESOLVE_OVERLOADED_PLUGIN'.  */
 
 static tree
-avr_resolve_overloaded_builtin (unsigned int iloc, tree fndecl, void *vargs)
+avr_resolve_overloaded_builtin (unsigned int iloc, tree fndecl, void *vargs,
+				bool)
 {
   tree type0, type1, fold = NULL_TREE;
   avr_builtin_id id = AVR_BUILTIN_COUNT;
diff --git a/gcc/config/riscv/riscv-c.cc b/gcc/config/riscv/riscv-c.cc
index 7e9c478e97b..34cc8cd96a7 100644
--- a/gcc/config/riscv/riscv-c.cc
+++ b/gcc/config/riscv/riscv-c.cc
@@ -289,7 +289,7 @@  riscv_pragma_intrinsic (cpp_reader *)
 /* Implement TARGET_CHECK_BUILTIN_CALL.  */
 static bool
 riscv_check_builtin_call (location_t loc, vec<location_t> arg_loc, tree fndecl,
-			  tree, unsigned int nargs, tree *args)
+			  tree, unsigned int nargs, tree *args, bool)
 {
   unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
   unsigned int subcode = code >> RISCV_BUILTIN_SHIFT;
@@ -308,7 +308,7 @@  riscv_check_builtin_call (location_t loc, vec<location_t> arg_loc, tree fndecl,
 /* Implement TARGET_RESOLVE_OVERLOADED_BUILTIN.  */
 static tree
 riscv_resolve_overloaded_builtin (unsigned int uncast_location, tree fndecl,
-				  void *uncast_arglist)
+				  void *uncast_arglist, bool)
 {
   vec<tree, va_gc> empty = {};
   location_t loc = (location_t) uncast_location;
diff --git a/gcc/config/rs6000/rs6000-c.cc b/gcc/config/rs6000/rs6000-c.cc
index 04882c396bf..c8369b51b84 100644
--- a/gcc/config/rs6000/rs6000-c.cc
+++ b/gcc/config/rs6000/rs6000-c.cc
@@ -1680,7 +1680,7 @@  find_instance (bool *unsupported_builtin, int *instance,
 
 tree
 altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
-				    void *passed_arglist)
+				    void *passed_arglist, bool)
 {
   rs6000_gen_builtins fcode
     = (rs6000_gen_builtins) DECL_MD_FUNCTION_CODE (fndecl);
diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h
index b40557a8557..3328a9ebe14 100644
--- a/gcc/config/rs6000/rs6000-protos.h
+++ b/gcc/config/rs6000/rs6000-protos.h
@@ -266,7 +266,8 @@  extern unsigned int rs6000_special_round_type_align (tree, unsigned int,
 						     unsigned int);
 extern unsigned int darwin_rs6000_special_round_type_align (tree, unsigned int,
 							    unsigned int);
-extern tree altivec_resolve_overloaded_builtin (location_t, tree, void *);
+extern tree
+altivec_resolve_overloaded_builtin (location_t, tree, void *, bool = true);
 extern rtx rs6000_libcall_value (machine_mode);
 extern rtx rs6000_va_arg (tree, tree);
 extern int function_ok_for_sibcall (tree);
diff --git a/gcc/config/s390/s390-c.cc b/gcc/config/s390/s390-c.cc
index 4521a86f048..9c833547d1e 100644
--- a/gcc/config/s390/s390-c.cc
+++ b/gcc/config/s390/s390-c.cc
@@ -884,9 +884,8 @@  s390_vec_n_elem (tree fndecl)
 /* Return a tree expression for a call to the overloaded builtin
    function OB_FNDECL at LOC with arguments PASSED_ARGLIST.  */
 tree
-s390_resolve_overloaded_builtin (location_t loc,
-				 tree ob_fndecl,
-				 void *passed_arglist)
+s390_resolve_overloaded_builtin (location_t loc, tree ob_fndecl,
+				 void *passed_arglist, bool)
 {
   vec<tree, va_gc> *arglist = static_cast<vec<tree, va_gc> *> (passed_arglist);
   unsigned int in_args_num = vec_safe_length (arglist);
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 309ab01d12d..aa829704a28 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -11168,7 +11168,8 @@  build_cxx_call (tree fn, int nargs, tree *argarray,
 	argarray[i] = maybe_constant_value (argarray[i]);
 
       if (!check_builtin_function_arguments (EXPR_LOCATION (fn), vNULL, fndecl,
-					     orig_fndecl, nargs, argarray))
+					     orig_fndecl, nargs, argarray,
+					     complain & tf_error))
 	return error_mark_node;
       else if (fndecl_built_in_p (fndecl, BUILT_IN_CLEAR_PADDING))
 	{
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0370d81de01..33ff7a90e6e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3195,7 +3195,8 @@  finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
       if (TREE_CODE (fn) == FUNCTION_DECL
 	  && (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL
 	      || DECL_BUILT_IN_CLASS (fn) == BUILT_IN_MD))
-	result = resolve_overloaded_builtin (input_location, fn, *args);
+	result = resolve_overloaded_builtin (input_location, fn, *args,
+					     complain & tf_error);
 
       if (!result)
 	{
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 4deb3d2c283..33a361880bc 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12108,7 +12108,7 @@  ignored.  This function should return the result of the call to the
 built-in function.
 @end deftypefn
 
-@deftypefn {Target Hook} tree TARGET_RESOLVE_OVERLOADED_BUILTIN (unsigned int @var{loc}, tree @var{fndecl}, void *@var{arglist})
+@deftypefn {Target Hook} tree TARGET_RESOLVE_OVERLOADED_BUILTIN (unsigned int @var{loc}, tree @var{fndecl}, void *@var{arglist}, bool @var{complain})
 Select a replacement for a machine specific built-in function that
 was set up by @samp{TARGET_INIT_BUILTINS}.  This is done
 @emph{before} regular type checking, and so allows the target to
@@ -12118,9 +12118,12 @@  arguments passed to the built-in function.  The result is a
 complete expression that implements the operation, usually
 another @code{CALL_EXPR}.
 @var{arglist} really has type @samp{VEC(tree,gc)*}
+@var{complain} is a boolean indicating whether invalid operations
+should emit errors.  This is set to @code{false} when the C++ templating
+context expects that errors should not be emitted (i.e. SFINAE).
 @end deftypefn
 
-@deftypefn {Target Hook} bool TARGET_CHECK_BUILTIN_CALL (location_t @var{loc}, vec<location_t> @var{arg_loc}, tree @var{fndecl}, tree @var{orig_fndecl}, unsigned int @var{nargs}, tree *@var{args})
+@deftypefn {Target Hook} bool TARGET_CHECK_BUILTIN_CALL (location_t @var{loc}, vec<location_t> @var{arg_loc}, tree @var{fndecl}, tree @var{orig_fndecl}, unsigned int @var{nargs}, tree *@var{args}, bool @var{complain})
 Perform semantic checking on a call to a machine-specific built-in
 function after its arguments have been constrained to the function
 signature.  Return true if the call is valid, otherwise report an error
@@ -12132,7 +12135,9 @@  but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}
 step is now to built-in function @var{fndecl}.  @var{loc} is the
 location of the call and @var{args} is an array of function arguments,
 of which there are @var{nargs}.  @var{arg_loc} specifies the location
-of each argument.
+of each argument.  @var{complain} is a boolean indicating whether invalid
+arguments should emitm errors.  This is set to @code{false} when the C++
+templating context expects that errors should not be emitted (i.e. SFINAE).
 @end deftypefn
 
 @deftypefn {Target Hook} tree TARGET_FOLD_BUILTIN (tree @var{fndecl}, int @var{n_args}, tree *@var{argp}, bool @var{ignore})
diff --git a/gcc/target.def b/gcc/target.def
index b3155010888..66c14dfc355 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2496,8 +2496,11 @@  declaration of the built-in function.  @var{arglist} is the list of\n\
 arguments passed to the built-in function.  The result is a\n\
 complete expression that implements the operation, usually\n\
 another @code{CALL_EXPR}.\n\
-@var{arglist} really has type @samp{VEC(tree,gc)*}",
- tree, (unsigned int /*location_t*/ loc, tree fndecl, void *arglist), NULL)
+@var{arglist} really has type @samp{VEC(tree,gc)*}\n\
+@var{complain} is a boolean indicating whether invalid operations\n\
+should emit errors.  This is set to @code{false} when the C++ templating\n\
+context expects that errors should not be emitted (i.e. SFINAE).",
+ tree, (unsigned int /*location_t*/ loc, tree fndecl, void *arglist, bool complain), NULL)
 
 DEFHOOK
 (check_builtin_call,
@@ -2512,9 +2515,11 @@  but after the optional @code{TARGET_RESOLVE_OVERLOADED_BUILTIN}\n\
 step is now to built-in function @var{fndecl}.  @var{loc} is the\n\
 location of the call and @var{args} is an array of function arguments,\n\
 of which there are @var{nargs}.  @var{arg_loc} specifies the location\n\
-of each argument.",
+of each argument.  @var{complain} is a boolean indicating whether invalid\n\
+arguments should emitm errors.  This is set to @code{false} when the C++\n\
+templating context expects that errors should not be emitted (i.e. SFINAE).",
  bool, (location_t loc, vec<location_t> arg_loc, tree fndecl,
-	tree orig_fndecl, unsigned int nargs, tree *args),
+	tree orig_fndecl, unsigned int nargs, tree *args, bool complain),
  NULL)
 
 /* Fold a target-specific builtin to a tree valid for both GIMPLE
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def
new file mode 100644
index 00000000000..6d6f9e52186
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads.def
@@ -0,0 +1,159 @@ 
+#include <type_traits>
+
+class X{};
+/* Want a zero-sized type in order to trigger one of the error messages.
+   Don't want the error message about creating a zero sized type.
+   However, *do* want to see any pedantic error messages coming from the rest
+   of the testcase.  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+class Zero {
+    unsigned int trailing[0];
+};
+#pragma GCC diagnostic pop
+class Large { public: int arr[10]; };
+class Incomplete;
+
+/* N.b. we have to use std::remove_pointer_t<T> for a few of the cases below to
+   handle using `int *` as the type.
+   In turn the problem is:
+     1) __atomic_load_n (int *, int) is valid, so when having `int *` as the
+	type to operate on does not create something invalid (which is the
+	point of the NONPOINTER_PARAMS entry).
+     2) __atomic_store_n (int *, int *, int) is not correct w.r.t. types
+	according to the GCC manual (and indeed ends up casting the pointer VAL
+	into an integer before storing it into the location.
+	However this is a known behaviour (PR 69404) so we're just working
+	around that for the moment.
+     3) __atomic_exchange_n and __atomic_compare_exchange_n, and all the
+	__atomic_fetch_<op> functions are using std::remove_pointer_t for
+	essentially the same behaviour of discarding the types as
+        __atomic_store_n.  */
+
+#define ATOMIC_SFINAES                                                      \
+  SFINAE_TYPE_CHECK(load_n,                                                 \
+		     (std::declval<T*>(), int()),                           \
+		     (std::declval<std::remove_pointer_t<T>> (), int()))    \
+  SFINAE_TYPE_CHECK(load,                                                   \
+		     (std::declval<T*>(), std::declval<T *>(), int()),      \
+		     (std::declval<T> (), std::declval<T*>(), int()))       \
+  SFINAE_TYPE_CHECK(store_n,                                                \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(store,                                                  \
+		     (std::declval<T*>(), std::declval<T *>(), int()),      \
+		     (std::declval<T> (), std::declval<T*>(), int()))       \
+  SFINAE_TYPE_CHECK(exchange_n,                                             \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(exchange,                                               \
+		     (std::declval<T*>(), std::declval<T *>(),              \
+		      std::declval<T*>(), int()),                           \
+		     (std::declval<T> (), std::declval<T*>(),               \
+		      std::declval<T*>(), int()))                           \
+  SFINAE_TYPE_CHECK(compare_exchange_n,                                     \
+		     (std::declval<T*>(), std::declval<T *>(),              \
+		      std::declval<T> (), bool(), int(), int()),            \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T*>(),               \
+		      std::declval<T> (), bool(), int(), int()))            \
+  SFINAE_TYPE_CHECK(compare_exchange,                                       \
+		     (std::declval<T*>(), std::declval<T *>(),              \
+		      std::declval<T*>(), bool(), int(), int()),            \
+		     (std::declval<T> (), std::declval<T*>(),               \
+		      std::declval<T*>(), bool(), int(), int()))            \
+  SFINAE_TYPE_CHECK(add_fetch,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(fetch_add,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(sub_fetch,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(fetch_sub,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(and_fetch,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(fetch_and,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(xor_fetch,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(fetch_xor,                                              \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(or_fetch,                                               \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(fetch_or,                                               \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(nand_fetch,                                             \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))        \
+  SFINAE_TYPE_CHECK(fetch_nand,                                             \
+		     (std::declval<T*>(), std::declval<T>(), int()),        \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T>(), int()))
+
+ATOMIC_SFINAES
+
+#define FETCH_OP_ASSERTS(NAME) \
+  MAKE_ATOMIC_ASSERT(NAME, int *, true) \
+  MAKE_ATOMIC_ASSERT(NAME, float, false) \
+  MAKE_ATOMIC_ASSERT(NAME, int, true) \
+  MAKE_ATOMIC_ASSERT(NAME, bool,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, X,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Zero,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Large,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Incomplete,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, long, true)
+
+#define ATOMIC_FETCH_ASSERTS \
+  FETCH_OP_ASSERTS(add_fetch) \
+  FETCH_OP_ASSERTS(fetch_add) \
+  FETCH_OP_ASSERTS(sub_fetch) \
+  FETCH_OP_ASSERTS(fetch_sub) \
+  FETCH_OP_ASSERTS(and_fetch) \
+  FETCH_OP_ASSERTS(fetch_and) \
+  FETCH_OP_ASSERTS(xor_fetch) \
+  FETCH_OP_ASSERTS(fetch_xor) \
+  FETCH_OP_ASSERTS(or_fetch) \
+  FETCH_OP_ASSERTS(fetch_or) \
+  FETCH_OP_ASSERTS(nand_fetch) \
+  FETCH_OP_ASSERTS(fetch_nand)
+
+#define ATOMIC_GENERIC_ASSERTS(NAME) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, int *, true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, int, true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, bool,  true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, X,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, Zero,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, Large,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, Incomplete,  false) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, long, true) \
+  MAKE_ATOMIC_ASSERT(NAME##_n, float, false) \
+  MAKE_ATOMIC_ASSERT(NAME, int *, true) \
+  MAKE_ATOMIC_ASSERT(NAME, int, true) \
+  MAKE_ATOMIC_ASSERT(NAME, bool,  true) \
+  MAKE_ATOMIC_ASSERT(NAME, X,  true) \
+  MAKE_ATOMIC_ASSERT(NAME, Zero,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, Large,  true) \
+  MAKE_ATOMIC_ASSERT(NAME, Incomplete,  false) \
+  MAKE_ATOMIC_ASSERT(NAME, float, true) \
+  MAKE_ATOMIC_ASSERT(NAME, long, true)
+
+
+#define ATOMIC_ASSERTS \
+  ATOMIC_FETCH_ASSERTS \
+  ATOMIC_GENERIC_ASSERTS(load) \
+  ATOMIC_GENERIC_ASSERTS(store) \
+  ATOMIC_GENERIC_ASSERTS(exchange) \
+  ATOMIC_GENERIC_ASSERTS(compare_exchange)
+
+int main() {
+    ATOMIC_ASSERTS
+    return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C
new file mode 100644
index 00000000000..b6b06c725f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads1.C
@@ -0,0 +1,24 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* builtin-atomic-overloads{1,2,3,4,5}.C are focussed on checking various
+   properties of all the different atomic builtins.
+   builtin-atomic-overloads6.C is focussed on checking all error conditions in
+   the code ignoring which builtin we trigger them with.  */
+
+/* Checks performed here:
+   Correctly specified -- as long as the type is something that these builtins
+   can work on.  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME PARAMS) >> \
+    : std::true_type {};
+
+/* Success according to type argument.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == SUCCESS);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C
new file mode 100644
index 00000000000..72131f5cc2b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads2.C
@@ -0,0 +1,18 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Parameters without a pointer where it should be.  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME NONPOINTER_PARAMS) >> \
+    : std::true_type {};
+
+/* Everything fails with pointer to non-pointer mismatch.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == false);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C
new file mode 100644
index 00000000000..e872cc4b553
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads3.C
@@ -0,0 +1,19 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Too many arguments (all atomic builtins take less than 7 arguments).  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME \
+			(int(), int(), int(), int(), int(), int(), std::declval<T>())) >> \
+  : std::true_type {};
+
+/* Everything fails with too many arguments.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == false);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C
new file mode 100644
index 00000000000..7fd3dcb56aa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads4.C
@@ -0,0 +1,19 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Too few arguments (all atomic functions require more than one argument).  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (std::declval<T>())) >> \
+    : std::true_type {};
+
+
+/* Everything fails with too few arguments.  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS) \
+  static_assert(is_##NAME##_available<TYPE>::value == false);
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C
new file mode 100644
index 00000000000..44ada61a574
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads5.C
@@ -0,0 +1,329 @@ 
+/* Check that overloaded builtins still error when not in SFINAE context.  */
+// { dg-do compile { target c++17 } }
+#include <type_traits>
+
+/* Should error here due to the fully specified (and invalid) builtin calls.  */
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, NONPOINTER_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (X(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (Incomplete(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (int(), int(), int(), int(), \
+					  int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME (std::declval<int*>(), int(), int(), int(), \
+					  int(), int(), int())) >> \
+    : std::true_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available<T, \
+    std::void_t<decltype(__atomic_##NAME ()) >> \
+    : std::true_type {};
+
+/* All the errors that are emitted.  We don't check that the errors directly
+   correspond to the relevant scenarios because validation that the correct
+   errors are generated for the relevant problems is done in other tests.
+
+   This test is checking that all the error types below are still emitted
+   when the problem occurs in templates for fully specified calls.
+
+   NOTE: We are missing some of the errors that could be emitted because the
+   above doesn't generate all invalid calls.
+   Things that could be added:
+      - pointer to incomplete type
+      - pointer to type of non-constant size
+      - pointer to type of zero size
+      - arguments after the first one:
+	- not pointers
+	- pointers to non-constant sized type
+	- pointers to function
+	- pointer to type of different size to the first one.
+	- pointer to const type
+	- pointer to volatile type
+      - memory model argument is not an integral type
+      - all errors around bitint
+      */
+
+/* { dg-error "argument 1 of '__atomic_compare_exchange' must be a non-void pointer type"                  "" { target *-*-* } 31 } */
+/* { dg-error "argument 1 of '__atomic_exchange' must be a non-void pointer type"                          "" { target *-*-* } 23 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a non-void pointer type"                              "" { target *-*-* } 19 } */
+/* { dg-error "argument 1 of '__atomic_store' must be a non-void pointer type"                             "" { target *-*-* } 19 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 19 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 23 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_compare_exchange'"                      "" { target *-*-* } 53 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 19 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 31 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_exchange'"                              "" { target *-*-* } 53 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 23 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 31 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                                  "" { target *-*-* } 53 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 11 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 15 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 23 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 27 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 31 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 35 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 39 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 43 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 48 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_store'"                                 "" { target *-*-* } 53 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_add_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_and_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_compare_exchange_n'" "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_exchange_n'"         "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_add'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_and'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_nand'"         "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_or'"           "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_sub'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_fetch_xor'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_load_n'"             "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_nand_fetch'"         "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_or_fetch'"           "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_store_n'"            "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_sub_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'Incomplete' is incompatible with argument 1 of '__atomic_xor_fetch'"          "" { target *-*-* } 39 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_add_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_and_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_compare_exchange_n'"        "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_exchange_n'"                "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_add'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_and'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_nand'"                "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_or'"                  "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_sub'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_fetch_xor'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"                    "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_nand_fetch'"                "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_or_fetch'"                  "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_store_n'"                   "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_sub_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 11 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 15 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 19 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 23 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 27 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 31 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_xor_fetch'"                 "" { target *-*-* } 43 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_add_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_and_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_compare_exchange_n'"          "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_exchange_n'"                  "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_add'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_and'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_nand'"                  "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_or'"                    "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_sub'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_fetch_xor'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_load_n'"                      "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_nand_fetch'"                  "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_or_fetch'"                    "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_store_n'"                     "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_sub_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "operand type 'X' is incompatible with argument 1 of '__atomic_xor_fetch'"                   "" { target *-*-* } 35 } */
+/* { dg-error "too few arguments to function '__atomic_add_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_and_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_compare_exchange_n'"                                "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_exchange_n'"                                        "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_add'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_and'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_nand'"                                        "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_or'"                                          "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_sub'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_fetch_xor'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_load_n'"                                            "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_nand_fetch'"                                        "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_or_fetch'"                                          "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_store_n'"                                           "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_sub_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too few arguments to function '__atomic_xor_fetch'"                                         "" { target *-*-* } 53 } */
+/* { dg-error "too many arguments to function '__atomic_add_fetch'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_and_fetch'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_compare_exchange_n'"                               "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_exchange_n'"                                       "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_add'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_and'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_nand'"                                       "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_or'"                                         "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_sub'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_fetch_xor'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_load_n'"                                           "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_nand_fetch'"                                       "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_or_fetch'"                                         "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_store_n'"                                          "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_sub_fetch'"                                        "" { target *-*-* } 48 } */
+/* { dg-error "too many arguments to function '__atomic_xor_fetch'"                                        "" { target *-*-* } 48 } */
+
+
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 11 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 15 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 19 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 23 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 27 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 31 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 35 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 39 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 43 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 48 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } 53 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 11 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 15 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 19 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 23 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 27 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 31 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 35 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 39 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 44 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 49 } */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } 53 } */
+
+/* Just avoid generating anything for the assertions (not what we're testing
+   here).  */
+#define MAKE_ATOMIC_ASSERT(NAME, TYPE, SUCCESS)
+
+#include "builtin-atomic-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C
new file mode 100644
index 00000000000..6ecf318b8c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads6.C
@@ -0,0 +1,171 @@ 
+/* Various atomic builtin errors are still emitted when in a fully specified
+   builtin in a template.  */
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-Wno-pedantic" }
+#include <type_traits>
+#ifdef __ARM_FEATURE_SVE
+#include <arm_sve.h>
+#endif
+
+/*
+   N.b. covering all if clauses, *not* all possible errors.
+   E.g. load, store, exchange, compare_exchange all go through
+   get_atomic_generic_size.  I ensure I test all if clauses in that function
+   but do not ensure each clause is hit when using each of the different
+   builtins.
+
+   N.b. this is the stuff that is not handled by
+   builtin-atomic-overloads{1,2,3,4,5}.C  */ 
+
+class X{};
+/* Want a zero-sized type in order to trigger one of the error messages.
+   Don't want the error message about creating a zero sized type.
+   However, *do* want to see any pedantic error messages coming from the rest
+   of the testcase.  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+class Zero {
+    unsigned int trailing[0];
+};
+#pragma GCC diagnostic pop
+class Large { public: int arr[10]; };
+class Incomplete;
+/* N.b. If there are other non-constant size types that I can use in a template
+   would appreciate hearing about it (especially if they work on all targets).
+   AFAIK VLA's are the other canonical example and have not managed to trigger
+   the same error with those due to scoping limitations.  */
+#ifdef __ARM_FEATURE_SVE
+    typedef __SVUint32_t NonConstant;
+#else
+class NonConstant { };
+#endif
+typedef __UINT64_TYPE__ uint64_t;
+typedef __UINT32_TYPE__ uint32_t;
+// typedef _BitInt(12) Bitint;
+
+
+#define INCORRECT_NUMBER_ARGUMENTS(X) \
+  X(load, (int(), int(), int(), int()), 0)
+
+#define NONPOINTER_FIRST_ARG(X) \
+  X(load, (int(), int(), int()), 1)
+#define INCOMPLETE_FIRST_ARG(X) \
+  X(load, (Incomplete(), int(), int()), 2)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_FIRST_ARG(X) \
+  X(load, (std::declval<NonConstant*>(), std::declval<NonConstant*>(), int()), 3)
+#define ZEROSIZE_FIRST_ARG(X) \
+  X(load, (std::declval<Zero*>(), std::declval<Zero*>(), int()), 4)
+
+// Errors triggered by a bad type in the first position not yet triggered by
+// builtin-atomic-overloads5.C.
+// N.b. these are already checked to *not* give an error in the SFINAE context
+// by builtin-atomic-overloads1.C.
+#define FIRST_ARGS_BADTYPE(X) \
+  ZEROSIZE_FIRST_ARG(X) \
+  NONCONST_SIZE_FIRST_ARG(X) \
+  INCOMPLETE_FIRST_ARG(X) \
+  NONPOINTER_FIRST_ARG(X)
+
+#define NONPOINTER_OTHER_ARG(X) \
+  X(load, (std::declval<int*>(), int(), int()), 5)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_OTHER_ARG(X) \
+  X(load, (std::declval<int*>(), std::declval<NonConstant*>(), int()), 6)
+#define FUNCTIONPTR_OTHER_ARG(X) \
+  X(load, (std::declval<int*>(), std::declval<int(*)()>(), int()), 7)
+#define SIZE_MISMATCH(X) \
+  X(load, (std::declval<uint32_t*>(), std::declval<uint64_t*>(), int()), 8)
+#define OUTPUT_CONST(X) \
+  X(load, (std::declval<int*>(), std::declval<const int*>(), int()), 9)
+#define SECOND_VOLATILE(X) \
+  X(load, (std::declval<int*>(), std::declval<volatile int*>(), int()), 10)
+
+#define OTHER_ARG_BADTYPE(X) \
+  NONPOINTER_OTHER_ARG(X) \
+  SECOND_VOLATILE(X) \
+  OUTPUT_CONST(X) \
+  SIZE_MISMATCH(X) \
+  FUNCTIONPTR_OTHER_ARG(X) \
+  NONCONST_SIZE_OTHER_ARG(X)
+
+#define MEMMODEL_BADTYPE(X) \
+  X(load, (std::declval<int*>(), std::declval<int*>(), float()), 11)
+#define MEMMODEL_TOOLARGE(X) \
+  X(load, (std::declval<int*>(), std::declval<int*>(), 100), 12)
+
+#define MEMMODEL_BAD(X) \
+  MEMMODEL_BADTYPE(X) \
+  MEMMODEL_TOOLARGE(X)
+
+#define GET_ATOMIC_GENERIC_ERRS(X) \
+  INCORRECT_NUMBER_ARGUMENTS(X) \
+  FIRST_ARGS_BADTYPE(X) \
+  OTHER_ARG_BADTYPE(X) \
+  MEMMODEL_BAD(X)
+
+#define SYNC_SIZE_TOOFEW(X) \
+  X(load_n, (), 0)
+#define SYNC_SIZE_INCOMPATIBLE(X) \
+  X(load_n, (int(), int()), 1)
+#define SYNC_SIZE_ERRS(X) \
+  SYNC_SIZE_TOOFEW(X) \
+  SYNC_SIZE_INCOMPATIBLE(X)
+
+#define SYNC_PARM_TOOFEW(X) \
+  X(load_n, (std::declval<int*>()), 2)
+#define SYNC_PARM_TOOMANY(X) \
+  X(load_n, (std::declval<int*>(), int(), int()), 3)
+#define SYNC_PARM_ERRS(X) \
+  SYNC_PARM_TOOFEW(X) \
+  SYNC_PARM_TOOMANY(X)
+
+/*
+   No Bitint in C++.  Hence can't check for this error.
+#define BITINT_FETCHCAS_TOOFEW(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>()))
+#define BITINT_FETCHCAS_TOOMANY(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>(), int(), int()))
+#define BITINT_FETCHCAS_ERRS(X) \
+  BITINT_FETCHCAS_TOOFEW(X) \
+  BITINT_FETCHCAS_TOOMANY(X)
+*/
+#define BITINT_FETCHCAS_ERRS(X)
+
+#define ALL_ERRS(X) \
+  GET_ATOMIC_GENERIC_ERRS(X) \
+  SYNC_SIZE_ERRS(X) \
+  SYNC_PARM_ERRS(X) \
+  BITINT_FETCHCAS_ERRS(X)
+
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, COUNTER) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available_##COUNTER : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available_##COUNTER<T, \
+    std::void_t<decltype(__atomic_##NAME PARAMS) >> \
+    : std::true_type {}; \
+
+ALL_ERRS(SFINAE_TYPE_CHECK)
+
+/* { dg-error "too few arguments to function '__atomic_load_n'"                          "" { target *-*-* } 108 } */
+/* { dg-error "operand type 'int' is incompatible with argument 1 of '__atomic_load_n'"  "" { target *-*-* } 110 } */
+/* { dg-error "too few arguments to function '__atomic_load_n'"                          "" { target *-*-* } 116 } */
+/* { dg-error "too many arguments to function '__atomic_load_n'"                         "" { target *-*-* } 118 } */
+/* { dg-error "template argument 1 is invalid"                                           "" { target *-*-* } 146 } */
+/* { dg-error "template argument 2 is invalid"                                           "" { target *-*-* } 146 } */
+/* { dg-error "incorrect number of arguments to function '__atomic_load'"                "" { target *-*-* } 48 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a non-void pointer type"            "" { target *-*-* } 51 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a non-void pointer type"            "" { target *-*-* } 53 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a pointer to a constant size type"  "" { target aarch64_sve } 56 } */
+/* { dg-error "argument 1 of '__atomic_load' must be a pointer to a nonzero size object" "" { target *-*-* } 58 } */
+/* { dg-error "argument 2 of '__atomic_load' must be a pointer type"                     "" { target *-*-* } 71 } */
+/* { dg-error "argument 2 of '__atomic_load' must be a pointer to a constant size type"  "" { target aarch64_sve } 74 } */
+/* { dg-error "size mismatch in argument 2 of '__atomic_load'"                           "" { target { ! aarch64_sve } } 74 } */
+/* { dg-error "argument 2 of '__atomic_load' must not be a pointer to a function"        "" { target *-*-* } 76 } */
+/* { dg-error "size mismatch in argument 2 of '__atomic_load'"                           "" { target *-*-* } 78 } */
+/* { dg-error "argument 2 of '__atomic_load' must not be a pointer to a 'const' type"    "" { target *-*-* } 80 } */
+/* { dg-error "argument 2 of '__atomic_load' must not be a pointer to a 'volatile' type" "" { target *-*-* } 82 } */
+/* { dg-error "non-integer memory model argument 3 of '__atomic_load'"                   "" { target *-*-* } 93 } */
+
+/* { dg-warning {invalid memory model argument 3 of '__atomic_load' \[-Winvalid-memory-model\]} "" { target *-*-* } 95 } */
diff --git a/gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C
new file mode 100644
index 00000000000..ef1d4627758
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-atomic-overloads7.C
@@ -0,0 +1,170 @@ 
+/* Various atomic builtin errors not emitted when in SFINAE context.  */
+// { dg-do compile { target c++17 } }
+// { dg-additional-options "-Wno-pedantic" }
+#include <type_traits>
+#ifdef __ARM_FEATURE_SVE
+#include <arm_sve.h>
+#endif
+
+/*
+   N.b. covering all if clauses, *not* all possible errors.
+   E.g. load, store, exchange, compare_exchange all go through
+   get_atomic_generic_size.  I ensure I test all if clauses in that function
+   but do not ensure each clause is hit when using each of the different
+   builtins.
+
+   N.b. this is the stuff that is not handled by
+   builtin-atomic-overloads{1,2,3,4,5}.C  */ 
+
+class X{};
+/* Want a zero-sized type in order to trigger one of the error messages.
+   Don't want the error message about creating a zero sized type.
+   However, *do* want to see any pedantic error messages coming from the rest
+   of the testcase (shouldn't be any, but would like to be alerted if there
+   are).  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+class Zero {
+    unsigned int trailing[0];
+};
+#pragma GCC diagnostic pop
+class Large { public: int arr[10]; };
+class Incomplete;
+/* N.b. If there are other non-constant size types that I can use in a template
+   would appreciate hearing about it (especially if they work on all targets).
+   AFAIK VLA's are the other canonical example and have not managed to trigger
+   the same error with those due to scoping limitations.  */
+#ifdef __ARM_FEATURE_SVE
+    typedef __SVUint32_t NonConstant;
+#else
+class NonConstant { };
+#endif
+typedef __UINT64_TYPE__ uint64_t;
+typedef __UINT32_TYPE__ uint32_t;
+// typedef _BitInt(12) Bitint;
+
+
+#define INCORRECT_NUMBER_ARGUMENTS(X) \
+  X(load, (std::declval<T>(), int(), int(), int()), 0)
+
+#define NONPOINTER_FIRST_ARG(X) \
+  X(load, (std::declval<std::remove_pointer_t<T>>(), int(), int()), 1)
+#define INCOMPLETE_FIRST_ARG(X) \
+  X(load, (std::declval<Incomplete*>(), std::declval<T>(), int()), 2)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_FIRST_ARG(X) \
+  X(load, (std::declval<NonConstant*>(), std::declval<T>(), int()), 3)
+#define ZEROSIZE_FIRST_ARG(X) \
+  X(load, (std::declval<Zero*>(), std::declval<T>(), int()), 4)
+
+// Errors triggered by a bad type in the first position not yet triggered by
+// builtin-atomic-overloads5.C.
+// N.b. these are already checked to *not* give an error in the SFINAE context
+// by builtin-atomic-overloads1.C.
+#define FIRST_ARGS_BADTYPE(X) \
+  ZEROSIZE_FIRST_ARG(X) \
+  NONCONST_SIZE_FIRST_ARG(X) \
+  INCOMPLETE_FIRST_ARG(X) \
+  NONPOINTER_FIRST_ARG(X)
+
+#define NONPOINTER_OTHER_ARG(X) \
+  X(load, (std::declval<T>(), int(), int()), 5)
+/* This won't trigger relevant fail when not using nonconstant sized type.  */
+#define NONCONST_SIZE_OTHER_ARG(X) \
+  X(load, (std::declval<T>(), std::declval<NonConstant*>(), int()), 6)
+#define FUNCTIONPTR_OTHER_ARG(X) \
+  X(load, (std::declval<T>(), std::declval<int(*)()>(), int()), 7)
+#define SIZE_MISMATCH(X) \
+  X(load, (std::declval<T>(), std::declval<uint64_t*>(), int()), 8)
+#define OUTPUT_CONST(X) \
+  X(load, (std::declval<T>(), std::declval<const int*>(), int()), 9)
+#define SECOND_VOLATILE(X) \
+  X(load, (std::declval<T>(), std::declval<volatile int*>(), int()), 10)
+
+#define OTHER_ARG_BADTYPE(X) \
+  NONPOINTER_OTHER_ARG(X) \
+  SECOND_VOLATILE(X) \
+  OUTPUT_CONST(X) \
+  SIZE_MISMATCH(X) \
+  FUNCTIONPTR_OTHER_ARG(X) \
+  NONCONST_SIZE_OTHER_ARG(X)
+
+#define MEMMODEL_BADTYPE(X) \
+  X(load, (std::declval<T>(), std::declval<int*>(), float()), 11)
+/* Don't include this in ALL_ERRS since this is a warning should still resolve
+   to `true` so we need a different ASSERT macro for it. */
+/* { dg-warning {invalid memory model argument 3 of '__atomic_load' \[-Winvalid-memory-model\]} "" { target *-*-* } .+2 } */
+#define MEMMODEL_TOOLARGE(X) \
+  X(load, (std::declval<T>(), std::declval<int*>(), 100), 12)
+
+#define MEMMODEL_BAD(X) \
+  MEMMODEL_BADTYPE(X) \
+
+#define GET_ATOMIC_GENERIC_ERRS(X) \
+  INCORRECT_NUMBER_ARGUMENTS(X) \
+  FIRST_ARGS_BADTYPE(X) \
+  OTHER_ARG_BADTYPE(X) \
+  MEMMODEL_BAD(X)
+
+/*  Can't trigger this error in SFINAE context since in order to trigger error
+    need zero arguments, but that means type is fully specified.
+    
+#define SYNC_SIZE_TOOFEW(X) \
+  X(load_n, (), 0)
+  */
+#define SYNC_SIZE_TOOFEW(X)
+#define SYNC_SIZE_INCOMPATIBLE(X) \
+  X(load_n, (int(), std::declval<T>()), 1)
+#define SYNC_SIZE_ERRS(X) \
+  SYNC_SIZE_TOOFEW(X) \
+  SYNC_SIZE_INCOMPATIBLE(X)
+
+#define SYNC_PARM_TOOFEW(X) \
+  X(load_n, (std::declval<T>()), 2)
+#define SYNC_PARM_TOOMANY(X) \
+  X(load_n, (std::declval<T>(), int(), int()), 3)
+#define SYNC_PARM_ERRS(X) \
+  SYNC_PARM_TOOFEW(X) \
+  SYNC_PARM_TOOMANY(X)
+
+/*
+   No Bitint in C++.  Hence can't check for this error.
+#define BITINT_FETCHCAS_TOOFEW(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>()))
+#define BITINT_FETCHCAS_TOOMANY(X) \
+  X(add_fetch, (std::declval<Bitint*>(), std::declval<Bitint>(), int(), int()))
+#define BITINT_FETCHCAS_ERRS(X) \
+  BITINT_FETCHCAS_TOOFEW(X) \
+  BITINT_FETCHCAS_TOOMANY(X)
+*/
+#define BITINT_FETCHCAS_ERRS(X)
+
+#define ALL_ERRS(X) \
+  GET_ATOMIC_GENERIC_ERRS(X) \
+  SYNC_SIZE_ERRS(X) \
+  SYNC_PARM_ERRS(X) \
+  BITINT_FETCHCAS_ERRS(X)
+
+#define SFINAE_TYPE_CHECK(NAME, PARAMS, COUNTER) \
+  template <typename T, typename = void> \
+  struct is_##NAME##_available_##COUNTER : std::false_type {}; \
+  template <typename T> \
+  struct is_##NAME##_available_##COUNTER<T, \
+    std::void_t<decltype(__atomic_##NAME PARAMS) >> \
+    : std::true_type {}; \
+
+ALL_ERRS(SFINAE_TYPE_CHECK)
+MEMMODEL_TOOLARGE(SFINAE_TYPE_CHECK)
+
+#define ASSERT(NAME, PARAMS, COUNTER) \
+  static_assert(is_##NAME##_available_##COUNTER<int*>::value == false);
+
+#define ASSERT_TRUE(NAME, PARAMS, COUNTER) \
+  static_assert(is_##NAME##_available_##COUNTER<int*>::value == true);
+
+int foo() {
+    ALL_ERRS(ASSERT)
+    MEMMODEL_TOOLARGE(ASSERT_TRUE)
+    return 1;
+}
+
diff --git a/gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C b/gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C
new file mode 100644
index 00000000000..1dbf4d7df0c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-sfinae-check-function-arguments.C
@@ -0,0 +1,145 @@ 
+/* Testing various builtins don't complain about incorrect number of arguments.  */
+// { dg-do compile { target c++17 } }
+#include <type_traits>
+
+/* Here checking the check_builtin_function_arguments function doesn't error
+   in an SFINAE context.  Test each of the errors that are directly emitted
+   from check_builtin_function_arguments.  */
+
+class BasicClass { };
+class Incomplete;
+enum En { En_A };
+
+/* N.b. Do not include tests against the *_overflow error message of storing to
+   atomic types since I don't know how to make a type that is both TYPE_ATOMIC
+   and INTEGRAL_TYPE_P in C++ (where _Atomic is not a keyword).
+   Similar for clear_padding error message on _Atomic integral types.  */
+#define NARGS_CHECKS(X)                                                        \
+  X (clrsbg, (std::declval<T> ()), unsigned, 4)                                \
+  X (ffsg, (std::declval<T> ()), unsigned, 4)                                  \
+  X (clzg, (std::declval<T> ()), int, 4)                                       \
+  X (ctzg, (std::declval<T> ()), int, 4)                                       \
+  X (parityg, (std::declval<T> ()), int, 4)                                    \
+  X (popcountg, (std::declval<T> ()), int, 4)                                  \
+  X (clzg, (std::declval<T> ()), bool, 3)                                      \
+  X (ctzg, (std::declval<T> ()), bool, 3)                                      \
+  X (clrsbg, (std::declval<T> ()), bool, 3)                                    \
+  X (ffsg, (std::declval<T> ()), bool, 3)                                      \
+  X (parityg, (std::declval<T> ()), bool, 3)                                   \
+  X (popcountg, (std::declval<T> ()), bool, 3)                                 \
+  X (clzg, (std::declval<T> ()), En, 2)                                        \
+  X (ctzg, (std::declval<T> ()), En, 2)                                        \
+  X (clrsbg, (std::declval<T> ()), En, 2)                                      \
+  X (ffsg, (std::declval<T> ()), En, 2)                                        \
+  X (parityg, (std::declval<T> ()), En, 2)                                     \
+  X (popcountg, (std::declval<T> ()), En, 2)                                   \
+  X (clzg, (std::declval<T> ()), float, 1)                                     \
+  X (ctzg, (std::declval<T> ()), float, 1)                                     \
+  X (clrsbg, (std::declval<T> ()), float, 1)                                   \
+  X (ffsg, (std::declval<T> ()), float, 1)                                     \
+  X (parityg, (std::declval<T> ()), float, 1)                                  \
+  X (popcountg, (std::declval<T> ()), float, 1)                                \
+  X (clzg, (std::declval<T> (), std::declval<long> ()), int, 101)              \
+  X (ctzg, (std::declval<T> (), std::declval<long> ()), int, 101)              \
+  X (clzg, (std::declval<T> (), std::declval<T> ()), float, 100)               \
+  X (ctzg, (std::declval<T> (), std::declval<T> ()), float, 100)               \
+  X (clear_padding, (std::declval<T *> ()), const int, 3)                      \
+  X (clear_padding, (std::declval<T *> ()), Incomplete, 2)                     \
+  X (clear_padding, (std::declval<T> ()), int, 1)                              \
+  X (add_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool> ()), int, 3)  \
+  X (sub_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool> ()), int, 3)  \
+  X (mul_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool> ()), int, 3)  \
+  X (add_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<En> ()), int, 2)    \
+  X (sub_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<En> ()), int, 2)    \
+  X (mul_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<En> ()), int, 2)    \
+  X (add_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), float, 1)   \
+  X (sub_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), float, 1)   \
+  X (mul_overflow_p,                                                           \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), float, 1)   \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<const int *> ()),   \
+     int, 5)                                                                   \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<const int *> ()),   \
+     int, 5)                                                                   \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<const int *> ()),   \
+     int, 5)                                                                   \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool *> ()), int,   \
+     4)                                                                        \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool *> ()), int,   \
+     4)                                                                        \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<bool *> ()), int,   \
+     4)                                                                        \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<En *> ()), int, 3)  \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<En *> ()), int, 3)  \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<En *> ()), int, 3)  \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), int, 2)     \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), int, 2)     \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T> ()), int, 2)     \
+  X (mul_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T *> ()), float, 1) \
+  X (sub_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T *> ()), float, 1) \
+  X (add_overflow,                                                             \
+     (std::declval<T> (), std::declval<T> (), std::declval<T *> ()), float, 1) \
+  X (assume_aligned, (std::declval<int *> (), int (), std::declval<T> ()),     \
+     float, 1)                                                                 \
+  X (fpclassify,                                                               \
+     (std::declval<T> (), int (), int (), int (), int (), std::declval<T> ()), \
+     int, 2)                                                                   \
+  X (fpclassify,                                                               \
+     (std::declval<T> (), int (), int (), int (), int (), float ()), float, 1) \
+  X (isgreater, (std::declval<T> (), std::declval<T> ()), int, 1)              \
+  X (isgreaterequal, (std::declval<T> (), std::declval<T> ()), int, 1)         \
+  X (isless, (std::declval<T> (), std::declval<T> ()), int, 1)                 \
+  X (islessequal, (std::declval<T> (), std::declval<T> ()), int, 1)            \
+  X (islessgreater, (std::declval<T> (), std::declval<T> ()), int, 1)          \
+  X (isunordered, (std::declval<T> (), std::declval<T> ()), int, 1)            \
+  X (iseqsig, (std::declval<T> (), std::declval<T> ()), int, 1)                \
+  X (isinf_sign, (std::declval<T> ()), int, 1)                                 \
+  X (isnan, (std::declval<T> ()), int, 1)                                      \
+  X (isnormal, (std::declval<T> ()), int, 1)                                   \
+  X (issignaling, (std::declval<T> ()), int, 1)                                \
+  X (signbit, (std::declval<T> ()), int, 1)                                    \
+  X (isinf, (std::declval<T> ()), int, 1)                                      \
+  X (isfinite, (std::declval<T> ()), int, 1)                                   \
+  X (alloca_with_align, (int (), int (), std::declval<T> ()), BasicClass, 1)   \
+  X (alloca_with_align_and_max, (std::declval<T> (), 1), int, 1)
+
+#define TEMPLATE_DEFS(NAME, PARAMS, TYPE, NUM)                                 \
+  template <typename T, typename = void>                                       \
+  struct is_##NAME##_available_##NUM : std::false_type                         \
+  {                                                                            \
+  };                                                                           \
+  template <typename T>                                                        \
+  struct is_##NAME##_available_##NUM<                                          \
+    T, std::void_t<decltype (__builtin_##NAME PARAMS)>> : std::true_type       \
+  {                                                                            \
+  };
+
+NARGS_CHECKS(TEMPLATE_DEFS)
+
+#define MAKE_ASSERT(NAME, PARAMS, TYPE, NUM) \
+  static_assert(is_##NAME##_available_##NUM<TYPE>::value == false);
+
+void foo() {
+    NARGS_CHECKS(MAKE_ASSERT)
+}
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def
new file mode 100644
index 00000000000..39d9b748d52
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads.def
@@ -0,0 +1,30 @@ 
+#include <type_traits>
+
+/* Use std::remove_pointer_t<T> here in order to make `int *` fail when using
+   the INVALID_PARAMETERS block.  Pointers of two different types are allowed
+   in __builtin_speculation_safe_value, it's one argument of a pointer and
+   another argument that is not a pointer that give errors.  */
+#define SPECULATION_SFINAES                                                    \
+  SFINAE_TYPE_CHECK ((std::declval<T> (), std::declval<T> ()),                 \
+		     (std::declval<T> ()),                                     \
+		     (std::declval<std::remove_pointer_t<T>> (), std::declval<T *> ()))
+
+SPECULATION_SFINAES
+
+class X{};
+class Large { public: int arr[10]; };
+class Incomplete;
+
+#define SPECULATION_ASSERTS                                                    \
+  MAKE_SPECULATION_ASSERT (int, true)                                          \
+  MAKE_SPECULATION_ASSERT (float, false)                                       \
+  MAKE_SPECULATION_ASSERT (X, false)                                           \
+  MAKE_SPECULATION_ASSERT (Large, false)                                       \
+  MAKE_SPECULATION_ASSERT (Incomplete, false)                                  \
+  MAKE_SPECULATION_ASSERT (int *, true)                                        \
+  MAKE_SPECULATION_ASSERT (long, true)
+
+int main() {
+    SPECULATION_ASSERTS
+    return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C
new file mode 100644
index 00000000000..bc8f1083a99
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads1.C
@@ -0,0 +1,18 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Various types (some that work, some that don't).  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value PARAMS) >> \
+    : std::true_type {};
+
+/* Success according to type of argument.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == SUCCESS);
+
+#include "builtin-speculation-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C
new file mode 100644
index 00000000000..842e349037c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads2.C
@@ -0,0 +1,19 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Invalid parameters with various types (mismatching pointer and non-pointer
+   types).  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value INVALID_PARAMS) >> \
+    : std::true_type {};
+
+/* Mismatching pointer/non-pointer typed parameters always fail.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == false);
+
+#include "builtin-speculation-overloads.def"
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C
new file mode 100644
index 00000000000..79956dd972f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads3.C
@@ -0,0 +1,20 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Too many arguments (for any type three arguments should be invalid).  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value \
+			 (int(), int(), std::declval<T>())) >> \
+    : std::true_type {};
+
+/* All types should fail with three arguments.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == false);
+
+#include "builtin-speculation-overloads.def"
+
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C
new file mode 100644
index 00000000000..c024a21fa18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads4.C
@@ -0,0 +1,19 @@ 
+/* Check that overloaded builtins can be used in templates with SFINAE.  */
+// { dg-do compile { target c++17 } }
+
+/* Checks performed here:
+   Optional parameter missing works same as with optional parameter specified.  */
+#define SFINAE_TYPE_CHECK(PARAMS, SHORTENED_PARAMS, INVALID_PARAMS) \
+  template <typename T, typename = void> \
+  struct is_available : std::false_type {}; \
+  template <typename T> \
+  struct is_available<T, \
+    std::void_t<decltype(__builtin_speculation_safe_value SHORTENED_PARAMS) >> \
+    : std::true_type {};
+
+/* All types should fail with three arguments.  */
+#define MAKE_SPECULATION_ASSERT(TYPE, SUCCESS) \
+  static_assert(is_available<TYPE>::value == SUCCESS);
+
+#include "builtin-speculation-overloads.def"
+
diff --git a/gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C
new file mode 100644
index 00000000000..8bccafb0e20
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-speculation-overloads5.C
@@ -0,0 +1,33 @@ 
+/* Check that overloaded builtins still error when not in SFINAE context.  */
+// { dg-do compile { target c++17 } }
+#include <type_traits>
+
+/* Checks performed here:
+   Fully specified and invalid function errors before SFINAE happens.  */
+template <typename T, typename = void> struct is_available : std::false_type
+{
+};
+
+/* Should be error here because of the fully specified (and invalid) builtin
+   call. */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } .+5 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } .+4 } */
+/* { dg-error "too few arguments to function" "" { target *-*-* } .+3 } */
+template <typename T>
+struct is_available<
+  T, std::void_t<decltype (__builtin_speculation_safe_value ())>>
+  : std::true_type
+{
+};
+
+/* Should be error here because of the fully specified (and invalid) builtin
+   call. */
+/* { dg-error "template argument 2 is invalid" "" { target *-*-* } .+5 } */
+/* { dg-error "template argument 1 is invalid" "" { target *-*-* } .+4 } */
+/* { dg-error "operand type 'float' is incompatible" "" { target *-*-* } .+3 } */
+template <typename T>
+struct is_available<
+  T, std::void_t<decltype (__builtin_speculation_safe_value (float()))>>
+  : std::true_type
+{
+};
diff --git a/gcc/testsuite/g++.dg/template/builtin-validate-nargs.C b/gcc/testsuite/g++.dg/template/builtin-validate-nargs.C
new file mode 100644
index 00000000000..c6a7aec48d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/builtin-validate-nargs.C
@@ -0,0 +1,67 @@ 
+/* Testing that we avoid error in SFINAE context when number of arguments are
+   invalid in a builtin template argument.  */
+// { dg-do compile { target c++17 } }
+/* { dg-additional-options "-Wno-macro-redefined" { target { *-*-* } } } */
+#include <type_traits>
+
+/* Here checking the builtin_function_validate_nargs function doesn't error
+   in an SFINAE context.  */
+
+template <typename T, typename = void> struct is_available : std::false_type
+{
+};
+
+/* Make one testcase for each of the functions in
+   check_builtin_function_arguments that uses builtin_function_validate_nargs.
+ */
+#define NARGS_CHECKS(X)                                                        \
+  X (constant_p, (std::declval<T> (), std::declval<T> ()))                     \
+  X (isfinite, (std::declval<T> (), std::declval<T> ()))                       \
+  X (isinf, (std::declval<T> (), std::declval<T> ()))                          \
+  X (isinf_sign, (std::declval<T> (), std::declval<T> ()))                     \
+  X (isnan, (std::declval<T> (), std::declval<T> ()))                          \
+  X (isnormal, (std::declval<T> (), std::declval<T> ()))                       \
+  X (issignaling, (std::declval<T> (), std::declval<T> ()))                    \
+  X (signbit, (std::declval<T> (), std::declval<T> ()))                        \
+  X (isgreater, (std::declval<T> ()))                                          \
+  X (isgreaterequal, (std::declval<T> ()))                                     \
+  X (isless, (std::declval<T> ()))                                             \
+  X (islessequal, (std::declval<T> ()))                                        \
+  X (islessgreater, (std::declval<T> ()))                                      \
+  X (isunordered, (std::declval<T> ()))                                        \
+  X (iseqsig, (std::declval<T> ()))                                            \
+  X (fpclassify, (std::declval<T> ()))                                         \
+  X (assume_aligned, (std::declval<T> ()))                                     \
+  X (add_overflow, (std::declval<T> ()))                                       \
+  X (sub_overflow, (std::declval<T> ()))                                       \
+  X (mul_overflow, (std::declval<T> ()))                                       \
+  X (add_overflow_p, (std::declval<T> ()))                                     \
+  X (sub_overflow_p, (std::declval<T> ()))                                     \
+  X (mul_overflow_p, (std::declval<T> ()))                                     \
+  X (clear_padding, (std::declval<T> (), std::declval<T> ()))                  \
+  X (clzg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))       \
+  X (ctzg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))       \
+  X (clrsbg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))     \
+  X (ffsg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))       \
+  X (parityg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))    \
+  X (popcountg, (std::declval<T> (), std::declval<T> (), std::declval<T> ()))
+
+#define TEMPLATE_DEFS(NAME, PARAMS)                                            \
+  template <typename T, typename = void>                                       \
+  struct is_##NAME##_available : std::false_type                               \
+  {                                                                            \
+  };                                                                           \
+  template <typename T>                                                        \
+  struct is_##NAME##_available<                                                \
+    T, std::void_t<decltype (__builtin_##NAME PARAMS)>> : std::true_type       \
+  {                                                                            \
+  };
+
+NARGS_CHECKS(TEMPLATE_DEFS)
+
+#define MAKE_ASSERT(NAME, PARAMS) \
+  static_assert(is_##NAME##_available<int>::value == false);
+
+void foo() {
+    NARGS_CHECKS(MAKE_ASSERT)
+}