From 8360528683f9538e8fb66c54fd50b2ac5bb639ba Mon Sep 17 00:00:00 2001 From: Christof Meerwald Date: Wed, 21 Oct 2026 11:30:50 +0000 Subject: [PATCH] Type transformations of qualified function types [GH #335] --- src/Changes | 12 ++ src/types.c | 25 +++- tests/expectations/edg_x86_64/imported.log | 1 - .../tests/changes/.gh-335.rto/default.1.1.txt | 1 + tests/tests/changes/gh-335.sft.cpp | 6 + .../gnu/cpp/ext/.decay.rto/default.1.1.txt | 5 - .../regressions/.gh-335.rto/default.1.1.txt | 1 + .../regressions/.gh-335.rto/default.2.1.txt | 1 + .../regressions/.gh-335.rto/default.3.1.txt | 1 + .../regressions/.gh-335.rto/default.4.1.txt | 1 + .../regressions/.gh-335.rto/default.5.1.txt | 1 + .../regressions/.gh-335.rto/default.6.1.txt | 1 + tests/tests/regressions/gh-335.sft.cpp | 113 ++++++++++++++++++ 13 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 tests/tests/changes/.gh-335.rto/default.1.1.txt create mode 100644 tests/tests/changes/gh-335.sft.cpp create mode 100644 tests/tests/regressions/.gh-335.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-335.rto/default.2.1.txt create mode 100644 tests/tests/regressions/.gh-335.rto/default.3.1.txt create mode 100644 tests/tests/regressions/.gh-335.rto/default.4.1.txt create mode 100644 tests/tests/regressions/.gh-335.rto/default.5.1.txt create mode 100644 tests/tests/regressions/.gh-335.rto/default.6.1.txt create mode 100644 tests/tests/regressions/gh-335.sft.cpp diff --git a/src/Changes b/src/Changes index fcbfc3d8b6..559372f7b0 100644 --- a/src/Changes +++ b/src/Changes @@ -1,5 +1,17 @@ Version 7.1 +10/21/26 [GH #335] +Type transformations of qualified function types + +Previously, the __add_lvalue_reference, __add_rvalue_reference, __add_pointer, +and __decay intrinsics produced incorrect types for function types with cv- or +ref-qualifiers. They now leave such types unchanged. For example, with +--c++11: + + using F = void() const; + using F = __add_pointer(F); // Previously a spurious error, now okay. + + 10/19/26 [GH #218] Reachability after a temporary with a "noreturn" constructor or destructor diff --git a/src/types.c b/src/types.c index 261861fe89..3e8a3aed1b 100644 --- a/src/types.c +++ b/src/types.c @@ -3580,6 +3580,22 @@ typeref entries representing typedefs or type operators. #if !STANDALONE_UTILITY_PROGRAM +static a_type_ptr add_pointer_to_type(a_type_ptr tp) +/* +Apply the transformation corresponding to std::add_pointer_t to tp and return +the resulting type. +*/ +{ + if (is_referenceable_type(tp) || is_void_type(tp)) { + if (is_reference_type(tp)) { + tp = type_pointed_to(tp); + } /* if */ + tp = make_pointer_type(tp); + } /* if */ + return tp; +} /* add_pointer_to_type */ + + a_type_ptr apply_type_transforming_intrinsic( a_type_ptr tp, a_typeref_kind kind, @@ -3641,10 +3657,7 @@ diagnostic in error cases. error_pos is the position to use for diagnostics result = remove_qualifiers(tp, TQ_VOLATILE); break; case trk_add_pointer: - if (is_reference_type(tp)) { - tp = type_pointed_to(tp); - } /* if */ - result = make_pointer_type(tp); + result = add_pointer_to_type(tp); break; case trk_remove_pointer: if (is_pointer_type(tp)) { @@ -3824,7 +3837,7 @@ Return TRUE if the given type is a referenceable type [defns.referenceable]. { tp = skip_typerefs(tp); return is_object_type(tp) || is_reference_ptr(tp) || - (is_function(tp) && !is_qualified_type(tp)); + (is_function(tp) && !is_qualified_function_type(tp)); } /* is_referenceable_type */ @@ -4080,7 +4093,7 @@ Apply the transformation corresponding to std::decay_t. if (is_array_type(tp)) { tp = make_pointer_type(array_element_type(tp)); } else if (is_function_type(tp)) { - tp = make_pointer_type(tp); + tp = add_pointer_to_type(tp); } else { tp = remove_qualifiers(tp, TQ_CONST | TQ_VOLATILE | TQ_RESTRICT); } /* if */ diff --git a/tests/expectations/edg_x86_64/imported.log b/tests/expectations/edg_x86_64/imported.log index 12f913d087..036be42be2 100644 --- a/tests/expectations/edg_x86_64/imported.log +++ b/tests/expectations/edg_x86_64/imported.log @@ -7339,7 +7339,6 @@ gnu/cpp/ext/constexpr-vla5.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL: gnu/cpp/ext/conv1.sft.cpp:fp:-DTEST_NUMBER=1 -w:FAIL: gnu/cpp/ext/conv2.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL: gnu/cpp/ext/conv3.sft.cpp:fp:-DTEST_NUMBER=1:FAIL: -gnu/cpp/ext/decay.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL: gnu/cpp/ext/decay1.sft.cpp:fn:-DTEST_NUMBER=1 --c++11:FAIL: gnu/cpp/ext/deprecate-1.sft.cpp:fp:-DTEST_NUMBER=1:FAIL: gnu/cpp/ext/desig9.sft.cpp:fn:-DTEST_NUMBER=1:FAIL: diff --git a/tests/tests/changes/.gh-335.rto/default.1.1.txt b/tests/tests/changes/.gh-335.rto/default.1.1.txt new file mode 100644 index 0000000000..6cbd97ece6 --- /dev/null +++ b/tests/tests/changes/.gh-335.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++11 Test_name.c diff --git a/tests/tests/changes/gh-335.sft.cpp b/tests/tests/changes/gh-335.sft.cpp new file mode 100644 index 0000000000..7f7f92c993 --- /dev/null +++ b/tests/tests/changes/gh-335.sft.cpp @@ -0,0 +1,6 @@ +//type:fp +//options:--c++11 +//remark:[GH #335] Type transformations of qualified function types + +using F = void() const; +using F = __add_pointer(F); // Previously a spurious error, now okay. diff --git a/tests/tests/imported/gnu/cpp/ext/.decay.rto/default.1.1.txt b/tests/tests/imported/gnu/cpp/ext/.decay.rto/default.1.1.txt index 03bd74c5ab..dcdf4c999a 100644 --- a/tests/tests/imported/gnu/cpp/ext/.decay.rto/default.1.1.txt +++ b/tests/tests/imported/gnu/cpp/ext/.decay.rto/default.1.1.txt @@ -1,6 +1 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics --c++11 Test_name.c -"Test_name.c", line 24: error: static assertion failed with "__is_same(test5_type, cfn_type)" - SA(__is_same(test5_type, cfn_type)); - ^ - -1 error detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-335.rto/default.1.1.txt b/tests/tests/regressions/.gh-335.rto/default.1.1.txt new file mode 100644 index 0000000000..6cbd97ece6 --- /dev/null +++ b/tests/tests/regressions/.gh-335.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++11 Test_name.c diff --git a/tests/tests/regressions/.gh-335.rto/default.2.1.txt b/tests/tests/regressions/.gh-335.rto/default.2.1.txt new file mode 100644 index 0000000000..d7c18a04c5 --- /dev/null +++ b/tests/tests/regressions/.gh-335.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++17 Test_name.c diff --git a/tests/tests/regressions/.gh-335.rto/default.3.1.txt b/tests/tests/regressions/.gh-335.rto/default.3.1.txt new file mode 100644 index 0000000000..f447571a87 --- /dev/null +++ b/tests/tests/regressions/.gh-335.rto/default.3.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=3 --gn 150200 --c++11 Test_name.c diff --git a/tests/tests/regressions/.gh-335.rto/default.4.1.txt b/tests/tests/regressions/.gh-335.rto/default.4.1.txt new file mode 100644 index 0000000000..7029c40442 --- /dev/null +++ b/tests/tests/regressions/.gh-335.rto/default.4.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=4 --gn 150200 --c++17 Test_name.c diff --git a/tests/tests/regressions/.gh-335.rto/default.5.1.txt b/tests/tests/regressions/.gh-335.rto/default.5.1.txt new file mode 100644 index 0000000000..6d797a36de --- /dev/null +++ b/tests/tests/regressions/.gh-335.rto/default.5.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=5 --clang_version 220100 --c++11 Test_name.c diff --git a/tests/tests/regressions/.gh-335.rto/default.6.1.txt b/tests/tests/regressions/.gh-335.rto/default.6.1.txt new file mode 100644 index 0000000000..8fd343a6fd --- /dev/null +++ b/tests/tests/regressions/.gh-335.rto/default.6.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=6 --clang_version 220100 --c++17 Test_name.c diff --git a/tests/tests/regressions/gh-335.sft.cpp b/tests/tests/regressions/gh-335.sft.cpp new file mode 100644 index 0000000000..ff672be535 --- /dev/null +++ b/tests/tests/regressions/gh-335.sft.cpp @@ -0,0 +1,113 @@ +//type:fp +//options:--c++11:--c++17:--gn 150200 --c++11:--gn 150200 --c++17:--clang_version 220100 --c++11:--clang_version 220100 --c++17 +//remark:Type-transforming intrinsics preserve qualified function types. + +template +struct is_same +{ + static constexpr bool value = false; +}; + +template +struct is_same +{ + static constexpr bool value = true; +}; + +// Exercise substitution as well as direct uses of the intrinsics. +template +struct Tester +{ + static_assert(is_same<__add_lvalue_reference(T), LVALUE_REF>::value, + "lvalue reference"); + static_assert(is_same<__add_rvalue_reference(T), RVALUE_REF>::value, + "rvalue reference"); + static_assert(is_same<__add_pointer(T), POINTER>::value, "pointer"); + static_assert(is_same<__decay(T), DECAY>::value, "decay"); +}; + +#define TEST(QUAL) \ + static_assert(sizeof(Tester) != 0, ""); \ + static_assert(sizeof(Tester) != 0, ""); + +namespace qualified_function_types +{ + TEST(const) + TEST(volatile) + TEST(const volatile) + TEST(&) + TEST(const &) + TEST(volatile &) + TEST(const volatile &) + TEST(&&) + TEST(const &&) + TEST(volatile &&) + TEST(const volatile &&) +} + +#if __cplusplus >= 201703L +namespace qualified_noexcept_function_types +{ + TEST(const noexcept) + TEST(volatile noexcept) + TEST(const volatile noexcept) + TEST(& noexcept) + TEST(const & noexcept) + TEST(volatile & noexcept) + TEST(const volatile & noexcept) + TEST(&& noexcept) + TEST(const && noexcept) + TEST(volatile && noexcept) + TEST(const volatile && noexcept) +} +#endif + +#undef TEST + +namespace alias_operands +{ + using F = void() const; + using G = void() &; + static_assert(is_same<__add_lvalue_reference(F), F>::value, ""); + static_assert(is_same<__add_rvalue_reference(G), G>::value, ""); + static_assert(is_same<__add_pointer(F), F>::value, ""); + static_assert(is_same<__decay(G), G>::value, ""); +} + +namespace object_and_reference_types +{ + Tester unqualified; + Tester qualified; + Tester lvalue; + Tester rvalue; +} + +namespace void_types +{ + Tester unqualified; + using V = const volatile void; + Tester qualified; +} + +namespace array_types +{ + Tester known_bound; + Tester unknown_bound; +} + +namespace unqualified_function_types +{ + using H = void(); + Tester function; + Tester lvalue; + Tester rvalue; +} + +#if __cplusplus >= 201703L +namespace unqualified_noexcept_function_types +{ + using N = void() noexcept; + Tester function; +} +#endif