public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of
@ 2021-08-13 20:22 officesamurai at gmail dot com
  2021-08-15  6:49 ` [Bug c++/101904] " redi at gcc dot gnu.org
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: officesamurai at gmail dot com @ 2021-08-13 20:22 UTC (permalink / raw)
  To: gcc-bugs

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

            Bug ID: 101904
           Summary: Wrong result of decltype during instantiation of
                    std::result_of
           Product: gcc
           Version: 11.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: officesamurai at gmail dot com
  Target Milestone: ---

gcc_wrong_decltype.cpp:
---
#include <type_traits>

struct ZZZ
{
    template <typename Func>
    auto foo(Func func) -> std::result_of_t<Func(int*)>
    {
        return func(static_cast<int*>(nullptr));
    }

    template <typename Func>
    auto foo(Func func) const -> std::result_of_t<Func(const int*)>
    {
        return func(static_cast<const int*>(nullptr));
    }
};

int main()
{
    const ZZZ zzz;

    zzz.foo(
        [&](auto* pointer)
            // specifying the return type explicitly will fix the issue
            //-> void
        {
            static_assert(std::is_same_v<decltype(pointer), const int*>, "");
        });
}
---

Compiler invocation:
---
$ g++-11.2.0 -c gcc_wrong_decltype.cpp 
gcc_wrong_decltype.cpp: In instantiation of ‘main()::<lambda(auto:1*)> [with
auto:1 = int]’:
/home/brd/soft/gcc-11.2.0/include/c++/11.2.0/type_traits:2466:26:   required by
substitution of ‘template<class _Fn, class ... _Args> static
std::__result_of_success<decltype (declval<_Fn>()((declval<_Args>)()...)),
std::__invoke_other> std::__result_of_other_impl::_S_test(int) [with _Fn =
main()::<lambda(auto:1*)>; _Args = {int*}]’
/home/brd/soft/gcc-11.2.0/include/c++/11.2.0/type_traits:2477:55:   required
from ‘struct std::__result_of_impl<false, false, main()::<lambda(auto:1*)>,
int*>’
/home/brd/soft/gcc-11.2.0/include/c++/11.2.0/type_traits:2482:12:   required
from ‘struct std::__invoke_result<main()::<lambda(auto:1*)>, int*>’
/home/brd/soft/gcc-11.2.0/include/c++/11.2.0/type_traits:2495:12:   required
from ‘struct std::result_of<main()::<lambda(auto:1*)>(int*)>’
/home/brd/soft/gcc-11.2.0/include/c++/11.2.0/type_traits:2530:11:   required by
substitution of ‘template<class _Tp> using result_of_t = typename
std::result_of::type [with _Tp = main()::<lambda(auto:1*)>(int*)]’
gcc_wrong_decltype.cpp:6:10:   required by substitution of ‘template<class
Func> std::result_of_t<Func(int*)> ZZZ::foo(Func) [with Func =
main()::<lambda(auto:1*)>]’
gcc_wrong_decltype.cpp:22:12:   required from here
gcc_wrong_decltype.cpp:27:32: error: static assertion failed
   27 |             static_assert(std::is_same_v<decltype(pointer), const
int*>, "");
      |                           ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc_wrong_decltype.cpp:27:32: note: ‘std::is_same_v<int*, const int*>’
evaluates to false
---

Note that 'pointer' actually points to const int (it's impossible to modify the
pointee). Also, explicitly specifying the return type for the lambda fixes the
issue.

Compiler version:
---
g++-11.2.0 -v
Using built-in specs.
COLLECT_GCC=g++-11.2.0
COLLECT_LTO_WRAPPER=/home/brd/soft/gcc-11.2.0/libexec/gcc/x86_64-pc-linux-gnu/11.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/home/brd/soft/gcc-11.2.0
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.2.0 (GCC) 
---

But the same happens with 10.3.0

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
@ 2021-08-15  6:49 ` redi at gcc dot gnu.org
  2021-08-15 20:42 ` officesamurai at gmail dot com
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2021-08-15  6:49 UTC (permalink / raw)
  To: gcc-bugs

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

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |RESOLVED
         Resolution|---                         |INVALID

--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Not a bug. The call to zzz.foo had to do overload resolution to decide which
foo to call. That means both result_of types get instantiated. That means the
lambda gets instantiated with an argument of int* as well as with an argument
of const int*. Without an explicit return type the body of the lambda is
instantiated, which fails the static_assert for the int* case

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
  2021-08-15  6:49 ` [Bug c++/101904] " redi at gcc dot gnu.org
@ 2021-08-15 20:42 ` officesamurai at gmail dot com
  2021-08-17  9:57 ` redi at gcc dot gnu.org
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: officesamurai at gmail dot com @ 2021-08-15 20:42 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #2 from Mikhail Kremniov <officesamurai at gmail dot com> ---
I see, thanks.
But I must mention that Clang is able to compile this code somehow.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
  2021-08-15  6:49 ` [Bug c++/101904] " redi at gcc dot gnu.org
  2021-08-15 20:42 ` officesamurai at gmail dot com
@ 2021-08-17  9:57 ` redi at gcc dot gnu.org
  2021-08-17 15:32 ` redi at gcc dot gnu.org
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2021-08-17  9:57 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #3 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Only clang can compile it. MSVC and EDG can't:
https://godbolt.org/z/941P1vfjG

I think Clang doesn't even bother to instantiate the return type for the
non-const overload, because it wouldn't be viable. I'm not sure if that's a
conforming implementation or not.

Reduced:

namespace std
{
    template<typename, typename> constexpr bool is_same_v = false;
    template<typename T> constexpr bool is_same_v<T, T> = true;

    template<typename> using void_t = void;

    template<typename T> T&& declval();

    template<typename F, typename Arg, typename = void>
        struct result_of_impl
        { };

    template<typename F, typename Arg>
        struct result_of_impl<F, Arg,
void_t<decltype(declval<F>()(declval<Arg>()))>>
        {
            using type = decltype(declval<F>()(declval<Arg>()));
        };

    template<typename> struct result_of;

    template<typename F, typename Arg>
        struct result_of<F(Arg)>
        : result_of_impl<F, Arg>
        { };

    template<typename T> using result_of_t = typename result_of<T>::type;
}


struct ZZZ
{
    template <typename Func>
    auto foo(Func func) -> std::result_of_t<Func(int*)>
    {
        return func(static_cast<int*>(nullptr));
    }

    template <typename Func>
    auto foo(Func func) const -> std::result_of_t<Func(const int*)>
    {
        return func(static_cast<const int*>(nullptr));
    }
};

int main()
{
    const ZZZ zzz;

    zzz.foo(
        [&](auto* pointer)
            // specifying the return type explicitly will fix the issue
            //-> void
        {
            static_assert(std::is_same_v<decltype(pointer), const int*>, "");
        });
}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
                   ` (2 preceding siblings ...)
  2021-08-17  9:57 ` redi at gcc dot gnu.org
@ 2021-08-17 15:32 ` redi at gcc dot gnu.org
  2021-08-24  3:58 ` ppalka at gcc dot gnu.org
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2021-08-17 15:32 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #3)
> I think Clang doesn't even bother to instantiate the return type for the
> non-const overload, because it wouldn't be viable. I'm not sure if that's a
> conforming implementation or not.

[temp.inst] p9:

"If the function selected by overload resolution (12.2) can be determined
without instantiating a class template definition, it is unspecified whether
that instantiation actually takes place."

So the compiler is permitted to skip the result_of instantiation in the return
type if it knows that overload won't be selected anyway. But it's not required
to skip it, so your code is still wrong.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
                   ` (3 preceding siblings ...)
  2021-08-17 15:32 ` redi at gcc dot gnu.org
@ 2021-08-24  3:58 ` ppalka at gcc dot gnu.org
  2021-09-03 15:34 ` cvs-commit at gcc dot gnu.org
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: ppalka at gcc dot gnu.org @ 2021-08-24  3:58 UTC (permalink / raw)
  To: gcc-bugs

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

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ppalka at gcc dot gnu.org

--- Comment #5 from Patrick Palka <ppalka at gcc dot gnu.org> ---
The validity of this testcase seems closely related to DR1391 which adds an
extra step during template argument deduction for checking the convertibility
of non-dependent function parms/args before instantiating the function
template.

Note that if we turn the two overloads of ZZZ::foo into static member functions
with an explicit 'this' parameter, then both GCC and Clang accept the testcase
as per DR1391:

#include <type_traits>

struct ZZZ
{
    template <typename Func>
    static auto foo(ZZZ*, Func func) -> std::result_of_t<Func(int*)>
    {
        return func(static_cast<int*>(nullptr));
    }

    template <typename Func>
    static auto foo(const ZZZ*, Func func) -> std::result_of_t<Func(const
int*)>
    {
        return func(static_cast<const int*>(nullptr));
    }
};

int main()
{
    const ZZZ zzz;

    ZZZ::foo(&zzz,
        [&](auto* pointer)
        {
            static_assert(std::is_same_v<decltype(pointer), const int*>, "");
        });
}


So it looks like Clang applies DR1391 to the implicit 'this' parameter of
member functions, which seems worthwhile for us to do too (though it's not
strictly required for conformance).  I'm working on a rough patch to that
effect

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
                   ` (4 preceding siblings ...)
  2021-08-24  3:58 ` ppalka at gcc dot gnu.org
@ 2021-09-03 15:34 ` cvs-commit at gcc dot gnu.org
  2021-09-13 15:14 ` ppalka at gcc dot gnu.org
  2021-09-15 22:42 ` cvs-commit at gcc dot gnu.org
  7 siblings, 0 replies; 9+ messages in thread
From: cvs-commit at gcc dot gnu.org @ 2021-09-03 15:34 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #6 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:47543e5f9d1fc502be79f91c87cbeb6eda17e641

commit r12-3346-g47543e5f9d1fc502be79f91c87cbeb6eda17e641
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri Sep 3 11:33:41 2021 -0400

    c++: shortcut bad convs during overload resolution [PR101904]

    In the context of overload resolution we have the notion of a "bad"
    argument conversion, which is a conversion that "would be a permitted
    with a bending of the language standards", and we handle such bad
    conversions specially.  In particular, we rank a bad conversion as
    better than no conversion but worse than a good conversion, and a bad
    conversion doesn't necessarily make a candidate unviable.  With the
    flag -fpermissive, we permit the situation where overload resolution
    selects a candidate that contains a bad conversion (which we call a
    non-strictly viable candidate).  And without the flag, the caller
    of overload resolution usually issues a distinct permerror in this
    situation instead.

    One consequence of this defacto behavior is that in order to distinguish
    a non-strictly viable candidate from an unviable candidate, if we
    encounter a bad argument conversion during overload resolution we must
    keep converting subsequent arguments because a subsequent conversion
    could render the candidate unviable instead of just non-strictly viable.
    But checking subsequent arguments can force template instantiations and
    result in otherwise avoidable hard errors.  And in particular, all
    'this' conversions are at worst bad, so this means the const/ref-qualifiers
    of a member function can't be used to prune a candidate quickly, which
    is the subject of the mentioned PR.

    This patch tries to improve the situation without changing the defacto
    output of add_candidates.  Specifically, when considering a candidate
    during overload resolution this patch makes us shortcut argument
    conversion checking upon encountering the first bad conversion
    (tentatively marking the candidate as non-strictly viable, though it
    could ultimately be unviable) under the assumption that we'll eventually
    find a strictly viable candidate anyway (which renders moot the
    distinction between non-strictly viable and unviable, since both are
    worse than a strictly viable candidate).  If this assumption turns out
    to be false, we'll fully reconsider the candidate under the defacto
    behavior (without the shortcutting) so that all its conversions are
    computed.

    So in the best case (there's a strictly viable candidate), we avoid
    some argument conversions and/or template argument deduction that may
    cause a hard error.  In the worst case (there's no such candidate), we
    have to redundantly consider some candidates twice.  (In a previous
    version of the patch, to avoid this redundant checking I created a new
    "deferred" conversion type that represents a conversion that is yet to
    be computed, and instead of reconsidering a candidate I just realized
    its deferred conversions.  But it doesn't seem this redundancy is a
    significant performance issue to justify the added complexity of this
    other approach.)

            PR c++/101904

    gcc/cp/ChangeLog:

            * call.c (build_this_conversion): New function, split out from
            add_function_candidate.
            (add_function_candidate): New parameter shortcut_bad_convs.
            Document it.  Use build_this_conversion.  Stop at the first bad
            argument conversion when shortcut_bad_convs is true.
            (add_template_candidate_real): New parameter shortcut_bad_convs.
            Use build_this_conversion to check the 'this' conversion before
            attempting deduction.  When the rejection reason code is
            rr_bad_arg_conversion, pass -1 instead of 0 as the viable
            parameter to add_candidate.  Pass 'convs' to add_candidate.
            (add_template_candidate): New parameter shortcut_bad_convs.
            (add_template_conv_candidate): Pass false as shortcut_bad_convs
            to add_template_candidate_real.
            (add_candidates): Prefer to shortcut bad conversions during
            overload resolution under the assumption that we'll eventually
            see a strictly viable candidate.  If this assumption turns out
            to be false, re-process the non-strictly viable candidates
            without shortcutting those bad conversions.

    gcc/testsuite/ChangeLog:

            * g++.dg/template/conv17.C: New test.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
                   ` (5 preceding siblings ...)
  2021-09-03 15:34 ` cvs-commit at gcc dot gnu.org
@ 2021-09-13 15:14 ` ppalka at gcc dot gnu.org
  2021-09-15 22:42 ` cvs-commit at gcc dot gnu.org
  7 siblings, 0 replies; 9+ messages in thread
From: ppalka at gcc dot gnu.org @ 2021-09-13 15:14 UTC (permalink / raw)
  To: gcc-bugs

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

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         Resolution|INVALID                     |FIXED
   Target Milestone|---                         |12.0

--- Comment #7 from Patrick Palka <ppalka at gcc dot gnu.org> ---
Changing the resolution to "fixed" for the record, since after the above commit
we now accept such code (which is permitted but not required by the standard).

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [Bug c++/101904] Wrong result of decltype during instantiation of std::result_of
  2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
                   ` (6 preceding siblings ...)
  2021-09-13 15:14 ` ppalka at gcc dot gnu.org
@ 2021-09-15 22:42 ` cvs-commit at gcc dot gnu.org
  7 siblings, 0 replies; 9+ messages in thread
From: cvs-commit at gcc dot gnu.org @ 2021-09-15 22:42 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #8 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:22806064a67cf30599957c1ffb322aa30e9e57e7

commit r12-3559-g22806064a67cf30599957c1ffb322aa30e9e57e7
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Sep 15 18:41:21 2021 -0400

    c++: shortcut bad convs during overload resolution, part 2 [PR101904]

    The r12-3346 change makes us avoid computing excess argument conversions
    during overload resolution, but only when it turns out there's a
    strictly viable candidate in the overload set.  If there's no such
    candidate then we still need to compute more conversions than strictly
    necessary because subsequent conversions after the first bad conversion
    can turn a non-strictly viable candidate into an unviable one, and that
    affects the outcome of overload resolution and the behavior of its
    callers (because of -fpermissive).

    But at least in a SFINAE context, the distinction between a non-strictly
    viable and an unviable candidate shouldn't matter all that much since
    performing a bad conversion is always an error (even with -fpermissive),
    and so forming a call to a non-strictly viable candidate will end up
    being a SFINAE error anyway, just like in the unviable case.  Hence a
    non-strictly viable candidate is effectively unviable (in a SFINAE
    context), and we don't really need to distinguish between the two kinds.
    We can take advantage of this observation to avoid computing excess
    argument conversions even when there's no strictly viable candidate in
    the overload set.

    This patch implements this idea.  We usually detect a SFINAE context by
    looking for the absence of the tf_error flag, but that's not specific
    enough: we can also get here from build_user_type_conversion with
    tf_error cleared, and there the distinction between a non-strictly
    viable candidate and an unviable candidate still matters (it determines
    whether a user-defined conversion is bad or just doesn't exist).  So this
    patch sets and checks for the tf_conv flag to detect this situation too,
    which avoids regressing conv2.C below.

    Unlike the previous change, this one does affect the outcome of overload
    resolution, but it should do so only in a way that preserves backwards
    compatibility with -fpermissive.

            PR c++/101904

    gcc/cp/ChangeLog:

            * call.c (build_user_type_conversion_1): Add tf_conv to complain.
            (add_candidates): When in a SFINAE context, instead of adding a
            candidate to bad_fns just mark it unviable.

    gcc/testsuite/ChangeLog:

            * g++.dg/ext/conv2.C: New test.
            * g++.dg/template/conv17.C: Extend test.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-09-15 22:42 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-13 20:22 [Bug c++/101904] New: Wrong result of decltype during instantiation of std::result_of officesamurai at gmail dot com
2021-08-15  6:49 ` [Bug c++/101904] " redi at gcc dot gnu.org
2021-08-15 20:42 ` officesamurai at gmail dot com
2021-08-17  9:57 ` redi at gcc dot gnu.org
2021-08-17 15:32 ` redi at gcc dot gnu.org
2021-08-24  3:58 ` ppalka at gcc dot gnu.org
2021-09-03 15:34 ` cvs-commit at gcc dot gnu.org
2021-09-13 15:14 ` ppalka at gcc dot gnu.org
2021-09-15 22:42 ` cvs-commit at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).