We've been rejecting the following valid code since GCC 4

=== cut here ===
struct A {
  explicit A (int);
  operator void* () const;
};
void foo (const A& x) {
  auto res = 0 ? x : 0;
}
int main () {
  A a{5};
  foo(a);
}
=== cut here ===

The problem is that for COND_EXPR, add_builtin_candidate has an early
return if the true and false values are not pointers that does not take
null pointer constants into account. This causes to not find any valid
conversion, and fail to compile.

This patch fixes the condition to also pass if the true/false values are
not pointers but null pointer constants, which resolves the PR.

Successfully tested on x86_64-pc-linux-gnu. Given this regression's age,
I don't think it make much sense to fix it during stage 4 (let me know
if you disagree), so OK for GCC16?

        PR c++/118282

gcc/cp/ChangeLog:

        * call.cc (add_builtin_candidate): Also check for null_ptr_cst_p
        operands.

gcc/testsuite/ChangeLog:

        * g++.dg/conversion/op8.C: New test.

---
 gcc/cp/call.cc                        |  3 +-
 gcc/testsuite/g++.dg/conversion/op8.C | 75 +++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/conversion/op8.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index c08bd0c8634..e440d58141b 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -3272,7 +3272,8 @@ add_builtin_candidate (struct z_candidate **candidates, 
enum tree_code code,
        break;
 
       /* Otherwise, the types should be pointers.  */
-      if (!TYPE_PTR_OR_PTRMEM_P (type1) || !TYPE_PTR_OR_PTRMEM_P (type2))
+      if (!((TYPE_PTR_OR_PTRMEM_P (type1) || null_ptr_cst_p (args[0]))
+           && (TYPE_PTR_OR_PTRMEM_P (type2) || null_ptr_cst_p (args[1]))))
        return;
 
       /* We don't check that the two types are the same; the logic
diff --git a/gcc/testsuite/g++.dg/conversion/op8.C 
b/gcc/testsuite/g++.dg/conversion/op8.C
new file mode 100644
index 00000000000..eac958776c9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/op8.C
@@ -0,0 +1,75 @@
+// PR c++/118282
+// { dg-do "compile" }
+
+#if __cplusplus >= 201103L
+# include <cstdint> // Only available from c++11 onwards.
+#endif
+
+struct A {
+  explicit A (int);
+  operator void* () const;
+};
+
+struct B {
+  explicit B (int);
+  operator char* () const;
+};
+
+struct C {
+  explicit C (int);
+  operator int () const;
+};
+
+struct BothWays {
+  BothWays (int);
+  operator void*() const;
+};
+
+extern bool my_bool;
+
+void foo (const A& a, const B& b, const C& c, const BothWays& d) {
+  void *res_a_1 = 0      ? 0 : a;
+  void *res_a_2 = 1      ? 0 : a;
+  void *res_a_3 = my_bool ? 0 : a;
+  void *res_a_4 = 0      ? a : 0;
+  void *res_a_5 = 1      ? a : 0;
+  void *res_a_6 = my_bool ? a : 0;
+
+  void *res_b_1 = 0      ? 0 : b;
+  void *res_b_2 = 1      ? 0 : b;
+  void *res_b_3 = my_bool ? 0 : b;
+  void *res_b_4 = 0      ? b : 0;
+  void *res_b_5 = 1      ? b : 0;
+  void *res_b_6 = my_bool ? b : 0;
+
+  //
+  // 0 valued constants that are NOT null pointer constants - this worked 
already.
+  //
+  char zero_char  = 0;
+  void *res_ko1          = 0     ? zero_char : a; // { dg-error "different 
types" }
+
+#if __cplusplus >= 201103L
+  // Those are only available starting with c++11.
+  int8_t zero_i8  = 0;
+  void *res_ko2          = 0     ? zero_i8   : a; // { dg-error "different 
types" "" { target c++11 }  }
+  uintptr_t zerop = 0;
+  void *res_ko3          = 0     ? zerop     : a; // { dg-error "different 
types" "" { target c++11 }  }
+#endif
+
+  // Conversion to integer - this worked already.
+  int res_int    = 0     ? 0 : c;
+
+  // Case where one arm is of class type that can be constructed from an
+  // integer and the other arm is a null pointer constant (inspired by
+  // g++.dg/template/cond5.C).
+  0 ? d : 0;
+  0 ? 0 : d;
+}
+
+int main(){
+  A a (5);
+  B b (42);
+  C c (43);
+  BothWays d (1982);
+  foo (a, b, c, d);
+}
-- 
2.44.0

Reply via email to