Bug 98283 - decltype(auto) may deduce a static data member to wrong type in a template
Summary: decltype(auto) may deduce a static data member to wrong type in a template
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: 14.0
Assignee: Patrick Palka
URL:
Keywords: accepts-invalid, wrong-code
Depends on:
Blocks: decltype
  Show dependency treegraph
 
Reported: 2020-12-15 01:13 UTC by Yongwei Wu
Modified: 2023-05-07 15:58 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-08-03 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Yongwei Wu 2020-12-15 01:13:57 UTC
The following code shows that `decltype(auto)` is deduced to the wrong type when used in template code for an expression like `(var.static_data_member>)`.

```cpp
#include <cstddef>
#include <type_traits>
#include <utility>

template <typename T>
struct type_displayer;

#define TYPE_DISPLAY(x) type_displayer<decltype(x)> test_obj;

struct SkipField {};

struct TestSkip {
    template <typename T, std::size_t> struct FIELD;

    int value;
    static SkipField unused;

    template <typename T>
    struct FIELD<T, 0> {
        T &&obj;
        FIELD(T &&ref) : obj(std::forward<T>(ref))
        {
        }
        decltype(auto) value()
        {
            return (obj.value);
        }
        static constexpr const char *name()
        {
            return "value";
        }
    };

    template <typename T>
    struct FIELD<T, 0 + 1> {
        T &&obj;
        FIELD(T &&ref) : obj(std::forward<T>(ref))
        {
        }
        decltype(auto) value()
        {
            return (obj.unused);
        }
        decltype((obj.unused)) valueAlt()
        {
            return (obj.unused);
        }
        static constexpr const char *name()
        {
            return "unused";
        }
    };
};

int main()
{
    TestSkip s;
    // decltype((TestSkip::FIELD<TestSkip&, 0>(s).value()))    is int&
    // decltype((TestSkip::FIELD<TestSkip&, 1>(s).value()))    is SkipField, but it should be SkipField&
    // decltype((TestSkip::FIELD<TestSkip&, 1>(s).valueAlt())) is SkipField&
}
```

This is a simplified usage scenario, where I want to use a macro to generate code to emulate static reflection. Here is the problematic part:

```cpp
        decltype(auto) value()
        {
            return (obj.unused);
        }
```

This function returns `SkipField`, instead of the expected `SkipField&`. I have verified that clang does what I want.

Currently I have to avoid `decltype(auto)` as a workaround:

```cpp
        decltype((obj.unused)) valueAlt()
        {
            return (obj.unused);
        }
```

(I used the macro TYPE_DISPLAY to check the type of an expression.)
Comment 1 Andrew Pinski 2021-08-03 05:33:37 UTC
Reduced testcase that is accepted but should not be:
struct TestSkip {
    static int unused;
};
template <class T>
struct FIELD {
    TestSkip *obj;
    decltype(auto) value()  {  return (obj->unused);  }
};
int main()
{
    TestSkip s;
    int sf;
    int i;
    decltype((FIELD<int>().value())) b;
}

Confirmed.
Comment 2 GCC Commits 2023-05-07 15:58:00 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:e4f1ea5d8b96fc9fbe4fc0e0e0a4938ceeef092f

commit r14-560-ge4f1ea5d8b96fc9fbe4fc0e0e0a4938ceeef092f
Author: Patrick Palka <ppalka@redhat.com>
Date:   Sun May 7 11:57:22 2023 -0400

    c++: parenthesized -> resolving to static data member [PR98283]
    
    Here we're neglecting to propagate parenthesized-ness when the
    member access (this->m) resolves to a static data member (and
    thus finish_class_member_access_expr yields a VAR_DECL instead
    of a COMPONENT_REF).
    
            PR c++/98283
    
    gcc/cp/ChangeLog:
    
            * pt.cc (tsubst_copy_and_build) <case COMPONENT_REF>: Propagate
            REF_PARENTHESIZED_P more generally via force_paren_expr.
            * semantics.cc (force_paren_expr): Document default argument.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp1y/paren6.C: New test.
Comment 3 Patrick Palka 2023-05-07 15:58:29 UTC
Fixed for GCC 14, thanks for the bug report