[clang] [Clang][Sema] Do not perform type conversion before computing result type of `x ? : y` in C++ (PR #108837)

Yanzuo Liu via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 12 03:09:44 PST 2025


https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/108837

>From 7e5f88c322852939ae68c65f6adf4a5d2973d095 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Mon, 16 Sep 2024 21:50:11 +0800
Subject: [PATCH 1/3] Fix computing result type of conditional operand

---
 clang/docs/ReleaseNotes.rst                |  2 ++
 clang/lib/Sema/SemaExpr.cpp                |  4 +---
 clang/test/SemaCXX/conditional-gnu-ext.cpp | 19 +++++++++++++++++++
 3 files changed, 22 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaCXX/conditional-gnu-ext.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 17ec1fe0b946de..db8a7568a12114 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -389,6 +389,8 @@ Bug Fixes to C++ Support
 - Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
   pack. #GH63819, #GH107560
 - Fix a crash when a static assert declaration has an invalid close location. (#GH108687)
+- Fixed a bug in computing result type of conditional operator with omitted middle operand
+  (a GNU extension). (#GH15998)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8f3e15cc9a9bb7..8414a55e4868b9 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8785,11 +8785,9 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc,
       commonExpr = result.get();
     }
     // We usually want to apply unary conversions *before* saving, except
-    // in the special case of a C++ l-value conditional.
+    // in the special case in C++ that operands have the same type.
     if (!(getLangOpts().CPlusPlus
           && !commonExpr->isTypeDependent()
-          && commonExpr->getValueKind() == RHSExpr->getValueKind()
-          && commonExpr->isGLValue()
           && commonExpr->isOrdinaryOrBitFieldObject()
           && RHSExpr->isOrdinaryOrBitFieldObject()
           && Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) {
diff --git a/clang/test/SemaCXX/conditional-gnu-ext.cpp b/clang/test/SemaCXX/conditional-gnu-ext.cpp
new file mode 100644
index 00000000000000..83a6fff8467863
--- /dev/null
+++ b/clang/test/SemaCXX/conditional-gnu-ext.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -fexperimental-new-constant-interpreter
+// expected-no-diagnostics
+
+/*
+FIXME: Support constexpr
+
+constexpr int evaluate_once(int x) {
+  return (++x) ? : 10;
+}
+static_assert(evaluate_once(0) == 1, "");
+*/
+
+namespace GH15998 {
+  enum E { Zero, One };
+  E test(E e) {
+    return e ? : One;
+  }
+}

>From 447c8b9c6e957308c9ff62e8c83b15b12f7fc1cb Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Mon, 16 Sep 2024 23:05:50 +0800
Subject: [PATCH 2/3] Format code

---
 clang/lib/Sema/SemaExpr.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8414a55e4868b9..c232d40ca31ac6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8786,11 +8786,10 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc,
     }
     // We usually want to apply unary conversions *before* saving, except
     // in the special case in C++ that operands have the same type.
-    if (!(getLangOpts().CPlusPlus
-          && !commonExpr->isTypeDependent()
-          && commonExpr->isOrdinaryOrBitFieldObject()
-          && RHSExpr->isOrdinaryOrBitFieldObject()
-          && Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) {
+    if (!(getLangOpts().CPlusPlus && !commonExpr->isTypeDependent() &&
+          commonExpr->isOrdinaryOrBitFieldObject() &&
+          RHSExpr->isOrdinaryOrBitFieldObject() &&
+          Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) {
       ExprResult commonRes = UsualUnaryConversions(commonExpr);
       if (commonRes.isInvalid())
         return ExprError();

>From 4c3dbacae0c8935384e1bfd39bf1397d5a81ad1d Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Sun, 12 Jan 2025 18:50:29 +0800
Subject: [PATCH 3/3] Fix previously undiscovered case and address review
 feedbacks

'conditional-gnu-exp.cpp' is mainly copied and modified from 'conditional-expr.cpp'
'conditional-gnu-ext.c' is mainly copied and modified from 'conditional-expr.c'
---
 clang/lib/Sema/SemaExpr.cpp                |  12 +-
 clang/test/CXX/drs/cwg5xx.cpp              |  12 +
 clang/test/Sema/conditional-expr.c         |   2 -
 clang/test/Sema/conditional-gnu-ext.c      | 111 ++++++
 clang/test/SemaCXX/conditional-expr.cpp    |  24 --
 clang/test/SemaCXX/conditional-gnu-ext.cpp | 442 ++++++++++++++++++++-
 6 files changed, 563 insertions(+), 40 deletions(-)
 create mode 100644 clang/test/Sema/conditional-gnu-ext.c

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c232d40ca31ac6..fd71c842d52110 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8785,11 +8785,8 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc,
       commonExpr = result.get();
     }
     // We usually want to apply unary conversions *before* saving, except
-    // in the special case in C++ that operands have the same type.
-    if (!(getLangOpts().CPlusPlus && !commonExpr->isTypeDependent() &&
-          commonExpr->isOrdinaryOrBitFieldObject() &&
-          RHSExpr->isOrdinaryOrBitFieldObject() &&
-          Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) {
+    // in special cases in C++.
+    if (!getLangOpts().CPlusPlus) {
       ExprResult commonRes = UsualUnaryConversions(commonExpr);
       if (commonRes.isInvalid())
         return ExprError();
@@ -8798,6 +8795,11 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc,
 
     // If the common expression is a class or array prvalue, materialize it
     // so that we can safely refer to it multiple times.
+    //
+    // FIXME: Materialization changes value catagory of the common expression,
+    // which may changes type and/or value catagory of the result of this
+    // operator. See tests in 'clang/test/SemaCXX/conditional-gnu-ext.cpp'.
+    // We need to confirm the behavior of GCC at first.
     if (commonExpr->isPRValue() && (commonExpr->getType()->isRecordType() ||
                                     commonExpr->getType()->isArrayType())) {
       ExprResult MatExpr = TemporaryMaterializationConversion(commonExpr);
diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp
index ed0c7159dfc889..d4ac004cbde08f 100644
--- a/clang/test/CXX/drs/cwg5xx.cpp
+++ b/clang/test/CXX/drs/cwg5xx.cpp
@@ -1138,6 +1138,18 @@ namespace cwg587 { // cwg587: 3.2
   struct S {};
   template void f(bool, const int, int);
   template void f(bool, const S, S);
+
+  void g(bool b, int i, const int ci) {
+    extern volatile int vi;
+    extern const volatile int cvi;
+
+    const int &cir = b ? i : ci;
+    volatile int &vir = b ? i : vi;
+    const volatile int &cvir1 = b ? ci : cvi;
+    const volatile int &cvir2 = b ? vi : cvi;
+    const volatile int &cvir3 = b ? ci : vi;
+    // expected-error at -1 {{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
+  }
 }
 
 namespace cwg588 { // cwg588: yes
diff --git a/clang/test/Sema/conditional-expr.c b/clang/test/Sema/conditional-expr.c
index b54b689ec4f055..153fe80912c83b 100644
--- a/clang/test/Sema/conditional-expr.c
+++ b/clang/test/Sema/conditional-expr.c
@@ -1,7 +1,6 @@
 // RUN: %clang_cc1 -fsyntax-only -Wno-pointer-to-int-cast -verify -pedantic -Wsign-conversion %s
 void foo(void) {
   *(0 ? (double *)0 : (void *)0) = 0;
-  // FIXME: GCC doesn't consider the following two statements to be errors.
   *(0 ? (double *)0 : (void *)(int *)0) = 0; /* expected-error {{incomplete type 'void' is not assignable}}
                                                 expected-warning {{ISO C does not allow indirection on operand of type 'void *'}} */
   *(0 ? (double *)0 : (void *)(double *)0) = 0; /* expected-error {{incomplete type 'void' is not assignable}}
@@ -95,7 +94,6 @@ int Postgresql(void) {
 extern int f1(void);
 
 int f0(int a) {
-  // GCC considers this a warning.
   return a ? f1() : nil; // expected-warning {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}} expected-error {{incompatible pointer to integer conversion returning 'void *' from a function with result type 'int'}}
 }
 
diff --git a/clang/test/Sema/conditional-gnu-ext.c b/clang/test/Sema/conditional-gnu-ext.c
new file mode 100644
index 00000000000000..a32bb98b591ece
--- /dev/null
+++ b/clang/test/Sema/conditional-gnu-ext.c
@@ -0,0 +1,111 @@
+// RUN: %clang_cc1 -fsyntax-only -Wno-pointer-to-int-cast -verify -pedantic -Wno-gnu-conditional-omitted-operand -Wsign-conversion %s
+
+void foo(void) {
+  *((double *)0 ? : (void *)0) = 0;
+  *((double *)0 ? : (void *)(int *)0) = 0;
+  // expected-error at -1 {{incomplete type 'void' is not assignable}}
+  // expected-warning at -2 {{ISO C does not allow indirection on operand of type 'void *'}}
+  *((double *)0 ? : (void *)(double *)0) = 0;
+  // expected-error at -1 {{incomplete type 'void' is not assignable}}
+  // expected-warning at -2 {{ISO C does not allow indirection on operand of type 'void *'}}
+  *((double *)0 ? : (int *)(void *)0) = 0;
+  // expected-error at -1 {{incomplete type 'void' is not assignable}}
+  // expected-warning at -2 {{pointer type mismatch ('double *' and 'int *')}}
+  // expected-warning at -3 {{ISO C does not allow indirection on operand of type 'void *'}}
+  *((double *)0 ? : (double *)(void *)0) = 0;
+
+  double *dp;
+  int *ip;
+  void *vp;
+
+  dp = (double *)0 ? : (void *)0;
+  vp = (double *)0 ? : (void *)0;
+  ip = (double *)0 ? : (void *)0; // expected-warning {{incompatible pointer types assigning to 'int *' from 'double *'}}
+
+  const int *cip;
+  vp = (vp ? : cip); // expected-warning {{discards qualifiers}}
+  vp = (cip ? : vp); // expected-warning {{discards qualifiers}}
+
+  int i = 2;
+  int (*pf)[2];
+  int (*pv)[i];
+  pf = (pf ? : pv);
+
+  enum {xxx, yyy, zzz} e, *ee;
+  short x;
+  ee = &x ? : &i ? : &e; // expected-warning {{pointer type mismatch}}
+
+  typedef void *asdf;
+  *((asdf) 0 ? : &x) = 10;
+
+  unsigned long test0 = 5;
+  test0 = (long) test0 ? : test0; // expected-warning {{operand of ? changes signedness: 'long' to 'unsigned long'}}
+  test0 = (int) test0 ? : test0; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+  test0 = (short) test0 ? : test0; // expected-warning {{operand of ? changes signedness: 'short' to 'unsigned long'}}
+  test0 = test0 ? : (long) test0; // expected-warning {{operand of ? changes signedness: 'long' to 'unsigned long'}}
+  test0 = test0 ? : (int) test0; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+  test0 = test0 ? : (short) test0; // expected-warning {{operand of ? changes signedness: 'short' to 'unsigned long'}}
+  test0 = test0 ? : (long) 10;
+  test0 = test0 ? : (int) 10;
+  test0 = test0 ? : (short) 10;
+  test0 = (long) 10 ? : test0;
+  test0 = (int) 10 ? : test0;
+  test0 = (short) 10 ? : test0;
+
+  int test1;
+  enum Enum { EVal };
+  test0 = EVal ? : test0;
+  test1 = EVal ? : (int) test0;
+  test0 = (unsigned) EVal ?
+                          : (int) test0;  // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+
+  test0 = EVal ? : test1; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+  test0 = test1 ? : EVal; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+
+  const int *const_int;
+  int *nonconst_int;
+  *(const_int ? : nonconst_int) = 42; // expected-error {{read-only variable is not assignable}}
+  *(nonconst_int ? : const_int) = 42; // expected-error {{read-only variable is not assignable}}
+
+  // The composite type here should be "int (*)[12]", fine for the sizeof
+  int (*incomplete)[];
+  int (*complete)[12];
+  sizeof(*(incomplete ? : complete)); // expected-warning {{expression result unused}}
+  sizeof(*(complete ? : incomplete)); // expected-warning {{expression result unused}}
+
+  int __attribute__((address_space(2))) *adr2;
+  int __attribute__((address_space(3))) *adr3;
+  adr2 ? : adr3; // expected-error {{conditional operator with the second and third operands of type  ('__attribute__((address_space(2))) int *' and '__attribute__((address_space(3))) int *') which are pointers to non-overlapping address spaces}}
+
+  // Make sure address-space mask ends up in the result type
+  ((adr2 ? : adr2) ? : nonconst_int); // expected-error {{conditional operator with the second and third operands of type  ('__attribute__((address_space(2))) int *' and 'int *') which are pointers to non-overlapping address spaces}}
+}
+
+int Postgresql(void) {
+  char x;
+  return (((*(&x) = ((char) 1)) ? : (void) ((void *) 0)), (unsigned long) ((void *) 0)); // expected-warning {{C99 forbids conditional expressions with only one void side}}
+}
+
+#define nil ((void*) 0)
+
+extern int f1(void);
+
+int f0(int a) {
+  return f1() ? : nil;
+  // expected-warning at -1 {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}}
+  // expected-error at -2 {{incompatible pointer to integer conversion returning 'void *' from a function with result type 'int'}}
+}
+
+int f2(int x) {
+  // We can suppress this because the immediate context wants an int.
+  return 0U ? : x;
+}
+
+#define NULL (void*)0
+
+void PR9236(void) {
+  struct A { int i; } A1;
+  (void)(NULL ? : A1); // expected-error {{non-pointer operand type 'struct A' incompatible with NULL}}
+  (void)(0 ? : A1); // expected-error {{incompatible operand types}}
+  (void)((void*)0 ? : A1); // expected-error {{incompatible operand types}}
+}
diff --git a/clang/test/SemaCXX/conditional-expr.cpp b/clang/test/SemaCXX/conditional-expr.cpp
index 01effaa189322b..700203452a0a52 100644
--- a/clang/test/SemaCXX/conditional-expr.cpp
+++ b/clang/test/SemaCXX/conditional-expr.cpp
@@ -355,30 +355,6 @@ namespace PR9236 {
   }
 }
 
-namespace DR587 {
-  template<typename T>
-  const T *f(bool b) {
-    static T t1 = T();
-    static const T t2 = T();
-    return &(b ? t1 : t2);
-  }
-  struct S {};
-  template const int *f(bool);
-  template const S *f(bool);
-
-  extern bool b;
-  int i = 0;
-  const int ci = 0;
-  volatile int vi = 0;
-  const volatile int cvi = 0;
-
-  const int &cir = b ? i : ci;
-  volatile int &vir = b ? vi : i;
-  const volatile int &cvir1 = b ? ci : cvi;
-  const volatile int &cvir2 = b ? cvi : vi;
-  const volatile int &cvir3 = b ? ci : vi; // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
-}
-
 namespace PR17052 {
   struct X {
     int i_;
diff --git a/clang/test/SemaCXX/conditional-gnu-ext.cpp b/clang/test/SemaCXX/conditional-gnu-ext.cpp
index 83a6fff8467863..264bad850a1d50 100644
--- a/clang/test/SemaCXX/conditional-gnu-ext.cpp
+++ b/clang/test/SemaCXX/conditional-gnu-ext.cpp
@@ -1,19 +1,443 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -fexperimental-new-constant-interpreter
-// expected-no-diagnostics
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify=expected,expected-cxx11 -std=c++11 -Wsign-conversion %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify=expected,expected-cxx11 -std=c++11 -Wsign-conversion %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify=expected,expected-cxx17 -std=c++17 -Wsign-conversion %s
+// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify=expected,expected-cxx17 -std=c++17 -Wsign-conversion %s -fexperimental-new-constant-interpreter
 
-/*
-FIXME: Support constexpr
+// FIXME: Fix 'FIXME' in `Sema::ActOnConditionalOp` in 'clang/lib/Sema/SemaExpr.cpp'
+// then remove `#if 0`
 
+struct B;
+struct A {
+  A();
+  A(const B&); // expected-note {{candidate constructor}}
+  explicit operator bool();
+};
+struct B {
+  operator A() const; // expected-note {{candidate function}}
+  explicit operator bool();
+};
+struct I { operator bool(); };
+struct J {
+  operator I();
+  explicit operator bool();
+};
+struct K { operator double(); };
+typedef void (*vfn)();
+struct F {
+  operator vfn();
+  explicit operator bool();
+};
+struct G { operator vfn(); };
+
+struct Base {
+  bool trick();
+  A trick() const;
+  void fn1();
+  explicit operator bool() const;
+};
+struct Derived : Base {
+  void fn2();
+  explicit operator bool() const;
+};
+struct Convertible {
+  operator Base&();
+  explicit operator bool();
+};
+struct Priv : private Base { explicit operator bool(); }; // expected-note 4 {{declared private here}}
+struct Mid : Base {};
+struct Fin : Mid, Derived { explicit operator bool(); };
+typedef void (Derived::*DFnPtr)();
+struct ToMemPtr { operator DFnPtr(); };
+
+struct BadDerived;
+struct BadBase {
+  operator BadDerived&();
+  explicit operator bool();
+};
+struct BadDerived : BadBase { explicit operator bool(); };
+
+struct Fields {
+  int i1, i2, b1 : 3;
+};
+struct MixedFields {
+  int i;
+  volatile int vi;
+  const int ci;
+  const volatile int cvi;
+};
+struct MixedFieldsDerived : MixedFields {};
+
+enum Enum { EVal };
+
+struct Ambig {
+  operator short(); // expected-note 2 {{candidate function}}
+  operator signed char(); // expected-note 2 {{candidate function}}
+  explicit operator bool();
+};
+
+struct Abstract {
+  virtual ~Abstract() = 0;
+  explicit operator bool() const;
+};
+
+struct Derived1: Abstract {};
+
+struct Derived2: Abstract {};
+
+#if __cplusplus >= 201402L
 constexpr int evaluate_once(int x) {
   return (++x) ? : 10;
 }
 static_assert(evaluate_once(0) == 1, "");
-*/
+#endif
+
+void p2() {
+  bool b = (bool)(A());
+
+  // one or both void, and throwing
+  b = b ? : throw 0;
+  b = b ? : (throw 0);
+  b ? : p2(); // expected-error {{right operand to ? is void, but left operand is of type 'bool'}}
+  (b ? : throw 0) = 0;
+  (b ? : (throw 0)) = 0;
+  (b ? : (void)(throw 0)) = 0; // expected-error {{right operand to ? is void, but left operand is of type 'bool'}}
+  bool &throwRef = (b ? : throw 0);
+}
+
+void p3() {
+  // one or both class type, convert to each other
+  // b1 (lvalues)
+  bool b;
+
+  Base base;
+  Derived derived;
+  Convertible conv;
+  Base &bar1 = base ? : derived;
+  Base &bar2 = derived ? : base;
+  Base &bar3 = base ? : conv;
+  Base &bar4 = conv ? : base;
+  // these are ambiguous
+  BadBase bb;
+  BadDerived bd;
+  (void)(bb ? : bd); // expected-error {{conditional expression is ambiguous; 'BadBase' can be converted to 'BadDerived' and vice versa}}
+  (void)(bd ? : bb); // expected-error {{conditional expression is ambiguous}}
+  (void)(BadBase() ? : BadDerived());
+  (void)(BadDerived() ? : BadBase());
+
+  // b2.1 (hierarchy stuff)
+  extern const Base constret();
+  extern const Derived constder();
+  // should use const overload
+  A a1((constret() ? : Base()).trick());
+  A a2((Base() ? : constret()).trick());
+  A a3((constret() ? : Derived()).trick());
+  A a4((Derived() ? : constret()).trick());
+  // should use non-const overload
+  b = (Base() ? : Base()).trick();
+  b = (Base() ? : Base()).trick();
+  b = (Base() ? : Derived()).trick();
+  b = (Derived() ? : Base()).trick();
+  // should fail: const lost
+  (void)(Base() ? : constder()); // expected-error {{incompatible operand types ('Base' and 'const Derived')}}
+  (void)(constder() ? : Base()); // expected-error {{incompatible operand types ('const Derived' and 'Base')}}
+
+  Priv priv;
+  Fin fin;
+  (void)(Base() ? : Priv()); // expected-error {{private base class}}
+  (void)(Priv() ? : Base()); // expected-error {{private base class}}
+  (void)(Base() ? : Fin()); // expected-error {{ambiguous conversion from derived class 'Fin' to base class 'Base':}}
+  (void)(Fin() ? : Base()); // expected-error {{ambiguous conversion from derived class 'Fin' to base class 'Base':}}
+  (void)(base ? : priv); // expected-error {{private base class}}
+  (void)(priv ? : base); // expected-error {{private base class}}
+  (void)(base ? : fin); // expected-error {{ambiguous conversion from derived class 'Fin' to base class 'Base':}}
+  (void)(fin ? : base); // expected-error {{ambiguous conversion from derived class 'Fin' to base class 'Base':}}
+
+  // b2.2 (non-hierarchy)
+  b = I() ? : b;
+  b = b ? : I();
+  I i1(I() ? : J());
+  I i2(J() ? : I());
+  // "the type [it] would have if E2 were converted to an rvalue"
+  vfn pfn = F() ? : p3;
+  using Tvfn = decltype(p3 ? : F());
+  using Tvfn = vfn;
+#if 0
+  (void)(A() ? : B()); // expected-error {{conversion from 'B' to 'A' is ambiguous}}
+#endif
+  (void)(B() ? : A()); // expected-error {{conversion from 'B' to 'A' is ambiguous}}
+  (void)(1 ? : Ambig()); // expected-error {{conversion from 'Ambig' to 'int' is ambiguous}}
+  (void)(Ambig() ? : 1); // expected-error {{conversion from 'Ambig' to 'int' is ambiguous}}
+  // By the way, this isn't an lvalue:
+  &(b ? : i1); // expected-error {{cannot take the address of an rvalue}}
+}
+
+void p4() {
+  // lvalue, same type
+  Fields flds;
+  int &ir = flds.i1 ? : flds.i2;
+  (flds.i1 ? : flds.b1) = 0;
+}
+
+void p5() {
+  // conversion to built-in types
+  double d = I() ? : K();
+  vfn pfn = F() ? : G();
+  DFnPtr pfm;
+  pfm = DFnPtr() ? : &Base::fn1;
+  pfm = &Base::fn1 ? : DFnPtr();
+}
+
+void p6(int i, int *pi, int &ir) {
+  // final conversions
+  i = i ? : ir;
+  pi = pi ? : 0;
+  pi = 0 ? : &i;
+  i = i ? : EVal;
+  i = EVal ? : i;
+  double d = 'c' ? : 4.0;
+  using Td = decltype('c' ? : 4.0);
+  using Td = decltype(4.0 ? : 'c');
+  using Td = double;
+  Base *pb = (Base*)0 ? : (Derived*)0;
+  pb = (Derived*)0 ? : (Base*)0;
+  DFnPtr pfm;
+  pfm = &Base::fn1 ? : &Derived::fn2;
+  pfm = &Derived::fn2 ? : &Base::fn1;
+  pfm = &Derived::fn2 ? : 0;
+  pfm = 0 ? : &Derived::fn2;
+  const int (MixedFieldsDerived::*mp1) =
+    &MixedFields::ci ? : &MixedFieldsDerived::i;
+  const volatile int (MixedFields::*mp2) =
+    &MixedFields::ci ? : &MixedFields::cvi;
+  (void)(&MixedFields::ci ? : &MixedFields::vi);
+  // Conversion of primitives does not result in an lvalue.
+  &(i ? : d); // expected-error {{cannot take the address of an rvalue}}
+
+  Fields flds;
+  (void)&(flds.i1 ? : flds.b1); // expected-error {{address of bit-field requested}}
+
+  unsigned long test0 = 5;
+  test0 = (long) test0 ? : test0; // expected-warning {{operand of ? changes signedness: 'long' to 'unsigned long'}}
+  test0 = (int) test0 ? : test0; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+  test0 = (short) test0 ? : test0; // expected-warning {{operand of ? changes signedness: 'short' to 'unsigned long'}}
+  test0 = test0 ? : (long) test0; // expected-warning {{operand of ? changes signedness: 'long' to 'unsigned long'}}
+  test0 = test0 ? : (int) test0; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+  test0 = test0 ? : (short) test0; // expected-warning {{operand of ? changes signedness: 'short' to 'unsigned long'}}
+  test0 = test0 ? : (long) 10;
+  test0 = test0 ? : (int) 10;
+  test0 = test0 ? : (short) 10;
+  test0 = (long) 10 ? : test0;
+  test0 = (int) 10 ? : test0;
+  test0 = (short) 10 ? : test0;
+
+  int test1;
+  test0 = EVal ? : test0;
+  test1 = EVal ? : (int) test0;
+
+  test0 = EVal ? : test1; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+  test0 = test1 ? : EVal; // expected-warning {{operand of ? changes signedness: 'int' to 'unsigned long'}}
+
+  test1 = EVal ? : (int) test0;
+  test1 = (int) test0 ? : EVal;
+
+  // Note the thing that this does not test: since DR446, various situations
+  // *must* create a separate temporary copy of class objects. This can only
+  // be properly tested at runtime, though.
+
+#if 0
+  const Abstract &abstract1 = static_cast<const Abstract&>(Derived1()) ? : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}}
+#endif
+  const Abstract &abstract2 = static_cast<const Abstract&>(Derived1()) ? : throw 3;
+}
+
+namespace PR6595 {
+  struct OtherString {
+    OtherString();
+    OtherString(const char*);
+    explicit operator bool();
+  };
+
+  struct String {
+    String(const char *);
+    String(const OtherString&);
+    operator const char*() const;
+    explicit operator bool();
+  };
+
+  void f(bool Cond, String S, OtherString OS) {
+    (void)(S ? : "");
+    (void)("" ? : S);
+    const char a[1] = {'a'};
+    (void)(S ? : a);
+    using T = decltype(a ? : S);
+    using T = String;
+    (void)(OS ? : S);
+  }
+}
+
+namespace PR6757 {
+  struct Foo1 {
+    Foo1();
+    Foo1(const Foo1&);
+  };
+
+  struct Foo2 { };
+
+#if 0
+  struct Foo3 {
+    Foo3(); // expected-note {{requires 0 arguments}}
+    Foo3(Foo3&); // expected-note {{would lose const qualifier}}
+  };
+#endif
+
+  struct Bar {
+    operator const Foo1&() const;
+    operator const Foo2&() const;
+#if 0
+    operator const Foo3&() const;
+#endif
+    explicit operator bool();
+  };
+
+  void f() {
+    (void)(Bar() ? : Foo1()); // okay
+    (void)(Bar() ? : Foo2()); // okay
+#if 0
+    (void)(Bar() ? : Foo3()); // expected-error {{no viable constructor copying temporary}}
+#endif
+  }
+}
+
+namespace test1 {
+  struct A {
+    enum Foo { fa };
+
+    Foo x();
+  };
+
+  void foo(int);
+
+  void test(A *a) {
+    foo(a->x() ? : 0);
+  }
+}
+
+namespace rdar7998817 {
+  class X {
+    X(X&); // expected-note {{declared private here}}
+
+    struct ref { };
+
+  public:
+    X();
+    X(ref);
+
+    operator ref();
+    explicit operator bool();
+  };
+
+  void f() {
+    X x;
+    (void)(x ? : X()); // expected-error {{calling a private constructor of class 'rdar7998817::X'}}
+  }
+}
+
+namespace PR7598 {
+  enum Enum {
+    v = 1,
+  };
+
+  const Enum g() {
+    return v;
+  }
+
+  const volatile Enum g2() {
+    return v;
+  }
+
+  void f() {
+    const Enum v2 = v;
+    Enum e = g() ? : v;
+    Enum e2 = v2 ? : v;
+    Enum e3 = g2() ? : v;
+  }
+
+}
+
+namespace PR9236 {
+#define NULL 0L
+  void f() {
+    int i;
+    (void)(A() ? : NULL); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
+    (void)(NULL ? : A()); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
+    (void)(0 ? : A()); // expected-error {{incompatible operand types}}
+    (void)(nullptr ? : A()); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
+    (void)(nullptr ? : i); // expected-error {{non-pointer operand type 'int' incompatible with nullptr}}
+    (void)(__null ? : A()); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
+    (void)((void*)0 ? : A()); // expected-error {{incompatible operand types}}
+  }
+}
+
+namespace cwg587 {
+  template<typename T> void f(T x, const T y) {
+    const T *p = &(x ? : y);
+  }
+  struct S { explicit operator bool(); };
+  template void f(int, const int);
+  template void f(S, const S);
+
+  void g(int i, const int ci, volatile int vi, const volatile int cvi) {
+    const int &cir = i ? : ci;
+    volatile int &vir = i ? : vi;
+    const volatile int &cvir1 = ci ? : cvi;
+    const volatile int &cvir2 = vi ? : cvi;
+    const volatile int &cvir3 = ci ? : vi; // expected-error {{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
+  }
+}
+
+namespace PR17052 {
+  struct X {
+    int i_;
+
+    int &test() { return i_ ? : throw 1; }
+  };
+}
+
+namespace PR26448 {
+struct Base { explicit operator bool(); } b;
+struct Derived : Base {} d;
+typedef decltype(static_cast<Base&&>(b) ? : static_cast<Derived&&>(d)) x;
+typedef Base &&x;
+}
+
+namespace lifetime_extension {
+  struct A { explicit operator bool(); };
+  struct B : A { B(); ~B(); };
+  struct C : A { C(); ~C(); };
+
+  void f() {
+    A &&r = static_cast<A&&>(B()) ? : static_cast<A&&>(C());
+  }
+
+  struct D {
+    A &&a;
+    explicit operator bool();
+  };
+  void f_indirect(bool b) {
+    D d = D{B()} ?
+                 : D{C()};
+    // expected-cxx11-warning at -1 {{temporary whose address is used as value of local variable 'd' will be destroyed at the end of the full-expression}}
+  }
+}
+
+namespace PR46484 {
+void g(int a, int b) {
+  long d = a = b ? : throw 0;
+}
+} // namespace PR46484
 
 namespace GH15998 {
-  enum E { Zero, One };
-  E test(E e) {
-    return e ? : One;
+  Enum test(Enum e) {
+    return e ? : EVal;
   }
 }



More information about the cfe-commits mailing list