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 <zw...@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 <zw...@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 <zw...@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@-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@-1 {{incomplete type 'void' is not assignable}} + // expected-warning@-2 {{ISO C does not allow indirection on operand of type 'void *'}} + *((double *)0 ? : (void *)(double *)0) = 0; + // expected-error@-1 {{incomplete type 'void' is not assignable}} + // expected-warning@-2 {{ISO C does not allow indirection on operand of type 'void *'}} + *((double *)0 ? : (int *)(void *)0) = 0; + // expected-error@-1 {{incomplete type 'void' is not assignable}} + // expected-warning@-2 {{pointer type mismatch ('double *' and 'int *')}} + // expected-warning@-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@-1 {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}} + // expected-error@-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@-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 <zw...@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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits