Message ID | 20241010091217.1150849-1-mmalcomson@nvidia.com |
---|---|
State | New |
Headers | show |
Series | c++: Allow overloaded builtins to be used in SFINAE context | expand |
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 --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) +}