[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
Tue Jan 14 01:21:22 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/4] 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/4] 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/4] 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;
}
}
>From d506fddef7a12e52a6feb08501fe4709cd50fb8e Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 14 Jan 2025 17:17:37 +0800
Subject: [PATCH 4/4] Fix failed codegen test
---
clang/test/CodeGenCXX/vector-size-conditional.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/test/CodeGenCXX/vector-size-conditional.cpp b/clang/test/CodeGenCXX/vector-size-conditional.cpp
index 033847cbb083ad..069cc101bf770d 100644
--- a/clang/test/CodeGenCXX/vector-size-conditional.cpp
+++ b/clang/test/CodeGenCXX/vector-size-conditional.cpp
@@ -150,12 +150,13 @@ void OneScalarOp() {
four_ints ?: some_float;
// CHECK: %[[COND:.+]] = load <4 x i32>
+ // CHECK: %[[LHS:.+]] = load <4 x i32>
// CHECK: %[[RHS:.+]] = load float
// CHECK: %[[RHS_CONV:.+]] = fptosi float %[[RHS]] to i32
// CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> poison, i32 %[[RHS_CONV]], i64 0
// CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i32> %[[RHS_SPLAT_INSERT]], <4 x i32> poison, <4 x i32> zeroinitializer
// CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer
- // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[COND]], <4 x i32> %[[RHS_SPLAT]]
+ // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS]], <4 x i32> %[[RHS_SPLAT]]
four_ints ? four_ints : 5.0f;
// CHECK: %[[COND:.+]] = load <4 x i32>
More information about the cfe-commits
mailing list