https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108169

mail at jhellings dot nl changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mail at jhellings dot nl

--- Comment #3 from mail at jhellings dot nl ---
I looked a bit further into this and into what the standard says. GCC seems to
derive the wrong type for non-type template parameters in a few cases both (1)
when they have a placeholder type such as auto and (2) when they use a normal
type. See https://jhellings.nl/article?articleid=1 for the full analysis.

My findings (summary):
Let V be a non-type template parameter. The C++20 standard defines decltype(V)
as follows (Clause 1 of Section [dcl.type.decltype]):

"For an expression E, the type denoted by decltype(E) is defined as follows:
... otherwise, if E is an unparenthesized id-expression naming a non-type
template-parameter, decltype(E) is the type of the template-parameter after
performing any necessary type deduction;"

In the above, the type of a template-parameter is defined as follows (Clause 6
of Section [temp.param]):

"A non-type template-parameter shall have one of the following (possibly
cv-qualified) types: ... The top-level cv-qualifiers on the template-parameter
are ignored when determining its type."

Hence, decltype(V) with V a non-type template parameter should never yield a
type with a top-level const qualifier.

In GCC 12 (x86-64 gcc 12.2 on Compiler Explorer), GCC does not follow this rule
in the following case: if the type of V is a structural class type T, then GCC
always yields a ``const T''.


See, for example, the following program _that does not use placeholder types_:

/*
 * @author{Jelle Hellings}.
 */
#include <iostream>
#include <typeinfo>
#include <type_traits>


/*
 * Format the type @{Type} and print it to standard output. The qualifiers
 * @{qs...} are used to include further details on @{Type}.
 */
template<class Type>
void type_printer(const auto&... qs)
{
    if constexpr (std::is_lvalue_reference_v<Type>) {
        return type_printer<std::remove_reference_t<Type>>(qs..., "&");
    }
    if constexpr (std::is_rvalue_reference_v<Type>) {
        return type_printer<std::remove_reference_t<Type>>(qs..., "&&");
    }
    if constexpr (std::is_const_v<Type>) {
        return type_printer<std::remove_const_t<Type>>(qs..., "const");
    }
    if constexpr (std::is_volatile_v<Type>) {
        return type_printer<std::remove_volatile_t<Type>>(qs..., "volatile");
    }
    if constexpr (std::is_array_v<Type>) {
        return type_printer<std::remove_extent_t<Type>>(qs..., "[]");
    }
    if constexpr (std::is_pointer_v<Type>) {
        return type_printer<std::remove_pointer_t<Type>>(qs..., "*");
    }
    ((std::cout << qs << " "), ...);
    std::cout << '`' << typeid(Type).name() << '`' << std::endl;
}


/*
 * Print the details on the type @{Type}, the type of a non-type template
 * parameter with value @{Value}, and on the type of expressions involving
 * @{Value}. The printed data will be labeled with @{case_name}.
 */
template<class Type, Type Value = Type{}>
void inspect_template_argument(const auto& case_name)
{
    type_printer<Type>(case_name);
    type_printer<decltype(Value)>(case_name);
}


/*
 * A dummy struct type.
 */
struct dummy {};


/*
 * Entry-point of the program.
 */
int main()
{
    /* We look at most combinations of types and values. */
    inspect_template_argument<int>("{int}");
    inspect_template_argument<const int>("{c-int}");
    inspect_template_argument<dummy>("{dummy}");
    inspect_template_argument<const dummy>("{c-dummy}");
}


The above program SHOULD return:

{int} `int`
{int} `int`
{c-int} const `int`
{c-int} `int`
{dummy} `5dummy`
{dummy} `5dummy`
{c-dummy} const `5dummy`
{c-dummy} `5dummy`

But, in x86-64 gcc 12.2 (on Compiler Explorer), it returns

{int} `i`
{int} `i`
{c-int} const `i`
{c-int} `i`
{dummy} `5dummy`
{dummy} const `5dummy`     << WRONG to the best of my understanding
{c-dummy} const `5dummy`
{c-dummy} const `5dummy`   << WRONG to the best of my understanding

Note that in x86-64 gcc 11.3 (on Compiler Explorer), the output was more-wrong,
as also the const-qualifier for non-class types was preserved in some cases.
Indeed, x86-64 gcc 11.3 (on Compiler Explorer) returns:

{int} `i`
{int} `i`
{c-int} const `i`
{c-int} const `i`         << WRONG & FIXED to the best of my understanding
{dummy} `5dummy`
{dummy} const `5dummy`    << WRONG to the best of my understanding
{c-dummy} const `5dummy`
{c-dummy} const `5dummy`  << WRONG to the best of my understanding



The issues detected above carry over to the usage of placeholder types, leading
to the bug reported in this bug report.

Reply via email to