Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Changes
Original file line number Diff line number Diff line change
@@ -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

Expand Down
25 changes: 19 additions & 6 deletions src/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 */


Expand Down Expand Up @@ -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 */
Expand Down
1 change: 0 additions & 1 deletion tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions tests/tests/changes/.gh-335.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++11 Test_name.c
6 changes: 6 additions & 0 deletions tests/tests/changes/gh-335.sft.cpp
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 0 additions & 5 deletions tests/tests/imported/gnu/cpp/ext/.decay.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -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".
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-335.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++11 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-335.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=2 --c++17 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-335.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=3 --gn 150200 --c++11 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-335.rto/default.4.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=4 --gn 150200 --c++17 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-335.rto/default.5.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=5 --clang_version 220100 --c++11 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-335.rto/default.6.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=6 --clang_version 220100 --c++17 Test_name.c
113 changes: 113 additions & 0 deletions tests/tests/regressions/gh-335.sft.cpp
Original file line number Diff line number Diff line change
@@ -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<typename, typename>
struct is_same
{
static constexpr bool value = false;
};

template<typename T>
struct is_same<T, T>
{
static constexpr bool value = true;
};

// Exercise substitution as well as direct uses of the intrinsics.
template<typename T, typename LVALUE_REF = T, typename RVALUE_REF = T,
typename POINTER = T, typename DECAY = T>
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<void() QUAL>) != 0, ""); \
static_assert(sizeof(Tester<void(int, ...) QUAL>) != 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<int, int&, int&&, int*, int> unqualified;
Tester<const int, const int&, const int&&, const int*, int> qualified;
Tester<const int&, const int&, const int&, const int*, int> lvalue;
Tester<int&&, int&, int&&, int*, int> rvalue;
}

namespace void_types
{
Tester<void, void, void, void*, void> unqualified;
using V = const volatile void;
Tester<V, V, V, V*, void> qualified;
}

namespace array_types
{
Tester<int[3], int(&)[3], int(&&)[3], int(*)[3], int*> known_bound;
Tester<int[], int(&)[], int(&&)[], int(*)[], int*> unknown_bound;
}

namespace unqualified_function_types
{
using H = void();
Tester<H, H&, H&&, H*, H*> function;
Tester<H&, H&, H&, H*, H*> lvalue;
Tester<H&&, H&, H&&, H*, H*> rvalue;
}

#if __cplusplus >= 201703L
namespace unqualified_noexcept_function_types
{
using N = void() noexcept;
Tester<N, N&, N&&, N*, N*> function;
}
#endif
Loading