diff --git a/src/Changes b/src/Changes index de5c529b76..62bc9a6a7b 100644 --- a/src/Changes +++ b/src/Changes @@ -1,3 +1,20 @@ +10/12/26 [GH #281] +Explicit conversion functions before parenthesized aggregate initialization + +The front end could incorrectly attempt parenthesized aggregate initialization +instead of considering an explicit conversion function when directly +initializing an aggregate in C++20 mode. For example: + + struct D { float x, y; }; + struct S { explicit operator D() const; }; + D f(S s) { + D d(s); // Previously an error in C++20 mode. Now okay. + return D(s); // Likewise. + } + +That is now fixed. The rule allowing explicit conversion functions in this +context comes from Core issue 899 and applies in C++11 and later modes. The +failure occurred when C++20 parenthesized aggregate initialization was enabled. Version 7.1 10/9/26 [GH #27] diff --git a/src/expr.c b/src/expr.c index fe8a6d57cd..fbe143f994 100644 --- a/src/expr.c +++ b/src/expr.c @@ -30702,11 +30702,16 @@ parentheses do not themselves correspond to aggregate initialization). an_arg_match_summary arg_match; an_operand opnd; clear_arg_match_summary(&arg_match); + /* When matching the first reference parameter of a copy constructor + called with one argument in a direct-initialization context, explicit + conversion functions are considered (N3092 [over.match.copy]/1, + Core issue 899). */ prep_list_initializer(arg_list, dest_type, /*is_direct_init=*/TRUE, /*check_narrowing=*/FALSE, /*warning_on_narrowing=*/FALSE, CCO_CAST | CCO_FUNC_NOTATION_CAST | - CCO_EXPLICIT_CAST | CCO_DIRECT_INITIALIZATION, + CCO_EXPLICIT_CAST | CCO_DIRECT_INITIALIZATION | + CCO_ALLOW_EXPLICIT_CONV_FUNCTIONS, /*fill_in_dtor=*/TRUE, /*force_temp=*/FALSE, /*make_lvalue_temp=*/FALSE, &opnd, (an_init_state*)NULL, &arg_match); diff --git a/src/overload.c b/src/overload.c index 57d3973e66..054c8fb3f4 100644 --- a/src/overload.c +++ b/src/overload.c @@ -27551,7 +27551,9 @@ will be an lvalue instead of the usual prvalue. (a_param_type_ptr)NULL, /*param_type_is_deduced=*/FALSE, try_user_conversions_in_ovl_res, - /*allow_expl_conv_funcs=*/FALSE, + /*allow_expl_conv_funcs=*/ + (conv_context & + CCO_ALLOW_EXPLICIT_CONV_FUNCTIONS) != 0, arg_match); } /* if */ } else { diff --git a/tests/tests/changes/.GH281.rto/default.1.1.txt b/tests/tests/changes/.GH281.rto/default.1.1.txt new file mode 100644 index 0000000000..fcf5b546e4 --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 -tused --c++11 Test_name.c diff --git a/tests/tests/changes/.GH281.rto/default.2.1.txt b/tests/tests/changes/.GH281.rto/default.2.1.txt new file mode 100644 index 0000000000..480ced1860 --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 -tused --c++11 --gnu_version 160200 Test_name.c diff --git a/tests/tests/changes/.GH281.rto/default.3.1.txt b/tests/tests/changes/.GH281.rto/default.3.1.txt new file mode 100644 index 0000000000..fcf16ce89d --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.3.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=3 -tused --c++11 --clang_version 230100 Test_name.c diff --git a/tests/tests/changes/.GH281.rto/default.4.1.txt b/tests/tests/changes/.GH281.rto/default.4.1.txt new file mode 100644 index 0000000000..7f68c48fcc --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.4.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=4 -tused --c++20 Test_name.c diff --git a/tests/tests/changes/.GH281.rto/default.5.1.txt b/tests/tests/changes/.GH281.rto/default.5.1.txt new file mode 100644 index 0000000000..5411ee9e60 --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.5.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=5 -tused --c++20 --gnu_version 160200 Test_name.c diff --git a/tests/tests/changes/.GH281.rto/default.6.1.txt b/tests/tests/changes/.GH281.rto/default.6.1.txt new file mode 100644 index 0000000000..87a47aca25 --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.6.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=6 -tused --c++20 --clang_version 230100 Test_name.c diff --git a/tests/tests/changes/.GH281.rto/default.7.1.txt b/tests/tests/changes/.GH281.rto/default.7.1.txt new file mode 100644 index 0000000000..ab7537ac29 --- /dev/null +++ b/tests/tests/changes/.GH281.rto/default.7.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=7 -tused --ms_c++20 --microsoft_version 1950 Test_name.c diff --git a/tests/tests/changes/GH281.sft.cpp b/tests/tests/changes/GH281.sft.cpp new file mode 100644 index 0000000000..4b5569e7bb --- /dev/null +++ b/tests/tests/changes/GH281.sft.cpp @@ -0,0 +1,20 @@ +//type:fp +//options:--c++11:--c++11 --gnu_version 160200:--c++11 --clang_version 230100:--c++20:--c++20 --gnu_version 160200:--c++20 --clang_version 230100:--ms_c++20 --microsoft_version 1950 +//options_all:-tused +//remark:Explicit conversion functions before aggregate fallback [GH #281] +// Core issue 899 permits these conversions in C++11 and later. Previously, +// C++20 parenthesized aggregate initialization caused spurious errors. + +struct D { float x, y; }; +struct S { + float x, y; + explicit constexpr operator D() const { return D{x, y}; } +}; + +D f(S s) { + D d(s); // Previously an error in C++20 mode. Now okay. + return D(s); // Likewise. +} + +constexpr S s = {1.0f, 2.0f}; +static_assert(D(s).x == 1.0f && D(s).y == 2.0f, "functional cast"); diff --git a/tests/tests/cwg/.899.rto/default.1.1.txt b/tests/tests/cwg/.899.rto/default.1.1.txt new file mode 100644 index 0000000000..04bdacfb33 --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.1.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=1 -tused --c++11 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/.899.rto/default.2.1.txt b/tests/tests/cwg/.899.rto/default.2.1.txt new file mode 100644 index 0000000000..22089b6dc8 --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.2.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=2 -tused --c++11 --gnu_version 160200 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/.899.rto/default.3.1.txt b/tests/tests/cwg/.899.rto/default.3.1.txt new file mode 100644 index 0000000000..7dc696587f --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.3.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=3 -tused --c++11 --clang_version 230100 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/.899.rto/default.4.1.txt b/tests/tests/cwg/.899.rto/default.4.1.txt new file mode 100644 index 0000000000..c88edeae40 --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.4.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=4 -tused --c++20 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/.899.rto/default.5.1.txt b/tests/tests/cwg/.899.rto/default.5.1.txt new file mode 100644 index 0000000000..cdaea3d1d1 --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.5.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=5 -tused --c++20 --gnu_version 160200 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/.899.rto/default.6.1.txt b/tests/tests/cwg/.899.rto/default.6.1.txt new file mode 100644 index 0000000000..cb2aec38b8 --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.6.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=6 -tused --c++20 --clang_version 230100 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/.899.rto/default.7.1.txt b/tests/tests/cwg/.899.rto/default.7.1.txt new file mode 100644 index 0000000000..09e46c8a0e --- /dev/null +++ b/tests/tests/cwg/.899.rto/default.7.1.txt @@ -0,0 +1,10 @@ +fe_only -DTEST_NUMBER=7 -tused --ms_c++20 --microsoft_version 1950 Test_name.c +"Test_name.c", line 18: error: no suitable conversion function from "A" to "int" exists + int i = a; // Error: copy-initialization. + ^ + +"Test_name.c", line 20: error: no suitable user-defined conversion from "A" to "C" exists + C c = a; // Error: copy-initialization. + ^ + +2 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/cwg/899.sft.cpp b/tests/tests/cwg/899.sft.cpp new file mode 100644 index 0000000000..9790365b97 --- /dev/null +++ b/tests/tests/cwg/899.sft.cpp @@ -0,0 +1,22 @@ +//type:fn +//options:--c++11:--c++11 --gnu_version 160200:--c++11 --clang_version 230100:--c++20:--c++20 --gnu_version 160200:--c++20 --clang_version 230100:--ms_c++20 --microsoft_version 1950 +//options_all:-tused +//remark:Core issue 899: Explicit conversion functions in direct class initialization + +struct C { }; +struct A { + explicit operator int() const; + explicit operator C() const; +}; + +struct B { + int i; + B(const A& a): i(a) { } // Okay. +}; + +void f(A a) { + int i = a; // Error: copy-initialization. + int j(a); // Okay: direct-initialization. + C c = a; // Error: copy-initialization. + C c2(a); // Okay: direct-initialization (Core issue 899). +} diff --git a/tests/tests/imported/clang/cpp/SemaCXX/.cxx2a-explicit-bool.rto/default.2.1.txt b/tests/tests/imported/clang/cpp/SemaCXX/.cxx2a-explicit-bool.rto/default.2.1.txt index 61b48306c1..e3487904a6 100644 --- a/tests/tests/imported/clang/cpp/SemaCXX/.cxx2a-explicit-bool.rto/default.2.1.txt +++ b/tests/tests/imported/clang/cpp/SemaCXX/.cxx2a-explicit-bool.rto/default.2.1.txt @@ -476,14 +476,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers Y y2 = z; // expected-error {{no viable conversion}} ^ -"Test_name.c", line 530: error: too many initializer values - Y y2b(z); - ^ - -"Test_name.c", line 532: error: too many initializer values - Y y4 = Y(z); - ^ - "Test_name.c", line 540: error: no suitable user-defined conversion from "conversion3::Z" to "const conversion3::Y" exists const Y& y6 = z; // expected-error {{no viable conversion}} ^ @@ -683,4 +675,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers explicit(ptr) S(long); // expected-error {{conversion from 'const int *' to 'bool' is not allowed in a converted constant expression}} ^ -121 errors detected in the compilation of "Test_name.c". +119 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/imported/clang/cpp/SemaCXX/.explicit.rto/default.2.1.txt b/tests/tests/imported/clang/cpp/SemaCXX/.explicit.rto/default.2.1.txt index ededac4caa..0428590258 100644 --- a/tests/tests/imported/clang/cpp/SemaCXX/.explicit.rto/default.2.1.txt +++ b/tests/tests/imported/clang/cpp/SemaCXX/.explicit.rto/default.2.1.txt @@ -23,14 +23,6 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers Y y2 = z; // expected-error {{no viable conversion from 'Z' to 'Y'}} ^ -"Test_name.c", line 90: error: too many initializer values - Y y2b(z); - ^ - -"Test_name.c", line 92: error: too many initializer values - Y y4 = Y(z); - ^ - "Test_name.c", line 100: error: no suitable user-defined conversion from "Z" to "const Y" exists const Y& y6 = z; // expected-error {{no viable conversion from 'Z' to 'const Y'}} ^ @@ -171,4 +163,4 @@ fe_only -DTEST_NUMBER=2 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers switch(EC){} ^ -31 errors detected in the compilation of "Test_name.c". +29 errors detected in the compilation of "Test_name.c".