Bug 90880 - compile error instead of SFINAE with non-public member variables
Summary: compile error instead of SFINAE with non-public member variables
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 9.1.0
: P3 normal
Target Milestone: 11.0
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2019-06-13 23:22 UTC by Federico Kircheis
Modified: 2020-05-01 20:39 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-06-13 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Federico Kircheis 2019-06-13 23:22:39 UTC
Hello, 

I searched for bugs related to SFINAE, and I do not think that this one has already reported:

----
#include <type_traits>
#include <cstdio>

template <typename T, typename = void> struct status : std::false_type{};

template <typename T> struct status<T, decltype(T::member, void())> : std::true_type {};

struct s1{int member;};
struct s2{int _member;};
class c1{int member;};
class c2{int _member;};
int main(){
	static_assert(status<s1>::value, "has member");
	static_assert(!status<s2>::value, "has no member");
	(void) status<c1>::value;
	//static_assert(status<c1>::value, "has member");
	static_assert(!status<c2>::value, "has no member");
}
-----

This code compiles fine with msvc and clang, but with g++ it triggers following compiler error:

--
bug.cpp: In substitution of ‘template<class T> struct status<T, decltype ((T::member, void()))> [with T = c1]’:                                                                                                     
bug.cpp:15:19:   required from here
bug.cpp:6:58: error: ‘int c1::member’ is private within this context
 template <typename T> struct status<T, decltype(T::member, void())> : std::true_type {};
                                                 ~~~~~~~~~^~~~~~~~
bug.cpp:10:14: note: declared private here
 class c1{int member;};
              ^~~~~~
bug.cpp: In instantiation of ‘struct status<c1>’:
bug.cpp:15:19:   required from here
bug.cpp:6:58: error: ‘int c1::member’ is private within this context
 template <typename T> struct status<T, decltype(T::member, void())> : std::true_type {};
                                                 ~~~~~~~~~^~~~~~~~
bug.cpp:10:14: note: declared private here
 class c1{int member;};
              ^~~~~~
bug.cpp:6:58: error: ‘int c1::member’ is private within this context
 template <typename T> struct status<T, decltype(T::member, void())> : std::true_type {};
                                                 ~~~~~~~~~^~~~~~~~
bug.cpp:10:14: note: declared private here
 class c1{int member;};
--

While the error message could be correct (more on that later), the issue is that because of SFINAE, this should never be compile-error (just like for `s2` and `c2`).


I've commented `static_assert(status<c1>::value, "has member");` out, because clang++ compiles `status<c1>::value` to false, while msvc to true.

I'm not 100% sure which of the compiler is correct, but `status<c1>::value` should definitively compile.
Comment 1 Federico Kircheis 2019-06-13 23:54:13 UTC
After researching a little bit more, I've convinced myself that `status<c1>::value` should be false, as `decltype` has no special rules for accessing private data, thus clang is correct.

If someone could confirm this, I could try to make a bug report to the msvc team
Comment 2 Jonathan Wakely 2019-06-13 23:59:15 UTC
(In reply to Federico Kircheis from comment #1)
> After researching a little bit more, I've convinced myself that
> `status<c1>::value` should be false, as `decltype` has no special rules for
> accessing private data, thus clang is correct.

Agreed. And G++ should accept it.
Comment 3 Federico Kircheis 2019-06-14 00:31:06 UTC
(In reply to Jonathan Wakely from comment #2)
> (In reply to Federico Kircheis from comment #1)
> > After researching a little bit more, I've convinced myself that
> > `status<c1>::value` should be false, as `decltype` has no special rules for
> > accessing private data, thus clang is correct.
> 
> Agreed. And G++ should accept it.


Thank you.

Sorry if linking to external bug trackers in comments is bad practice, but I did not saw any rule about it.
Since it could be interesting for those having this issue, here is the bug report for MSVC: 

https://developercommunity.visualstudio.com/content/problem/607019/incorrect-decltype-on-non-public-member-variables.html
Comment 4 Jonathan Wakely 2019-06-14 07:59:26 UTC
(In reply to Federico Kircheis from comment #3)
> Sorry if linking to external bug trackers in comments is bad practice, but I
> did not saw any rule about it.

There's no rule against it, it's useful.

> Since it could be interesting for those having this issue, here is the bug
> report for MSVC: 
> 
> https://developercommunity.visualstudio.com/content/problem/607019/incorrect-
> decltype-on-non-public-member-variables.html

Thanks.
Comment 5 GCC Commits 2020-05-01 20:39:05 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:4f6c1ca287d2c64856ef67fa50bc462633d5b8cf

commit r11-21-g4f6c1ca287d2c64856ef67fa50bc462633d5b8cf
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri May 1 16:18:19 2020 -0400

    c++: Missing SFINAE with inaccessible static data member [PR90880]
    
    This is a missing SFINAE issue when verifying the accessibility of a
    static data member.
    
    The cause is that check_accessibility_of_qualified_id unconditionally
    passes tf_warning_or_error to perform_or_defer_access_check, even when
    called from tsubst_qualified_id(..., complain=tf_none).
    
    This patch fixes this by plumbing 'complain' from tsubst_qualified_id
    through check_accessibility_of_qualified_id to reach
    perform_or_defer_access_check, and by giving
    check_accessibility_of_qualified_id the appropriate return value.
    
    gcc/cp/ChangeLog:
    
            PR c++/90880
            * cp-tree.h (check_accessibility_of_qualified_id): Add
            tsubst_flags_t parameter and change return type to bool.
            * parser.c (cp_parser_lookup_name): Pass tf_warning_to_error to
            check_accessibility_of_qualified_id.
            * pt.c (tsubst_qualified_id): Return error_mark_node if
            check_accessibility_of_qualified_id returns false.
            * semantics.c (check_accessibility_of_qualified_id): Add
            complain parameter.  Pass complain instead of
            tf_warning_or_error to perform_or_defer_access_check.  Return
            true unless perform_or_defer_access_check returns false.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/90880
            * g++.dg/template/sfinae29.C: New test.
Comment 6 Patrick Palka 2020-05-01 20:39:41 UTC
Fixed for GCC 11.