Bug 107773 - Class members do not hide inherited types inside requires
Summary: Class members do not hide inherited types inside requires
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 11.1.0
: P3 normal
Target Milestone: 13.0
Assignee: Patrick Palka
URL:
Keywords: accepts-invalid, rejects-valid, wrong-code
Depends on:
Blocks:
 
Reported: 2022-11-20 19:28 UTC by Daniel Eiband
Modified: 2025-10-07 17:19 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2022-11-20 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Eiband 2022-11-20 19:28:31 UTC
The following program exits with code 1.

test.cpp:

struct a
{
  using get = void;
};

struct b : a
{
  int get(int i) const;
};

template <typename T>
concept c = requires { typename T::get; };

int main()
{
  return c<b>;
}

$ g++ test.cpp -std=c++20
$ ./a.out
$ echo $?
1

Expected is exit code 0, because T::get is not a type for T=b.

The code snippet is based on user "apple apple"'s comment on https://stackoverflow.com/questions/74509418/why-does-member-function-not-hide-type-alias-in-base-class-when-compiling-with-g.

User "n. m." also notes that GCC happily compiles https://eel.is/c++draft/temp.res.general#example-4 which does not use concepts and should emit an error.

$ g++ -v
gcc version 11.1.0 (Ubuntu 11.1.0-1ubuntu1~20.04)
Comment 1 Andrew Pinski 2022-11-20 19:47:28 UTC
Confirmed, a compile time testcase:
struct a { using get = void; };
struct b : a { int get(int i); };
template <typename T>
concept c = requires { typename T::get; };
template <typename T> constexpr int f(T t) {return 0;}
template <c T> constexpr int f(T t) { typename T::get t1; return 1; }
static_assert(f(b{}) == 0);
Comment 2 Patrick Palka 2022-11-28 16:41:12 UTC
accepts-invalid c++98 testcase (not a regression):

struct a {
  typedef void get;
};

struct b : a {
  int get(int i) const;
};

template<class T>
void f() {
  typedef typename T::get type;
}

template void f<b>();
Comment 3 GCC Commits 2023-02-16 16:12:29 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:46711ff8e60d64b7e5550f4614c29d42b224f98b

commit r13-6098-g46711ff8e60d64b7e5550f4614c29d42b224f98b
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu Feb 16 11:12:19 2023 -0500

    c++: TYPENAME_TYPE lookup ignoring non-types [PR107773]
    
    Currently when resolving a TYPENAME_TYPE for 'typename T::m' via
    make_typename_type, we consider only type bindings of 'm' and ignore
    non-type ones.  But [temp.res.general]/3 says, in a note, "the usual
    qualified name lookup ([basic.lookup.qual]) applies even in the presence
    of 'typename'", and qualified name lookup doesn't discriminate between
    type and non-type bindings.  So when resolving such a TYPENAME_TYPE
    we want the lookup to consider all bindings.
    
    An exception is when we have a TYPENAME_TYPE corresponding to the
    qualifying scope of the :: scope resolution operator, such as 'T::type'
    in 'T::type::m'.  In that case, [basic.lookup.qual]/1 applies, and
    lookup for such a TYPENAME_TYPE must ignore non-type bindings.  So in
    order to correctly handle all cases, make_typename_type needs an
    additional flag controlling whether to restrict the lookup.
    
    To that end this patch adds a new tsubst flag tf_qualifying_scope
    denoting whether we're substituting the LHS of the :: operator,
    which make_typename_type will look for to conditionally restrict the
    lookup to type bindings (by default we want to consider all bindings).
    So in contexts such as substituting into the scope of TYPENAME_TYPE,
    SCOPE_REF or USING_DECL we simply pass tf_qualifying_scope to the
    relevant tsubst / tsubst_copy call, and if that scope is a TYPENAME_TYPE
    then make_typename_type will restrict the lookup accordingly.  This flag
    is intended to apply only to overall scope (TYPENAME_TYPE or not) so we
    must be careful to clear the flag to avoid propagating it when recursing
    into sub-trees of the scope.
    
            PR c++/107773
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (enum tsubst_flags): New flag tf_qualifying_scope.
            * decl.cc (make_typename_type): Use lookup_member instead of
            lookup_field.  If tf_qualifying_scope is set, pass want_type=true
            instead of =false to lookup_member.  Generalize format specifier
            in diagnostic to handle both type and non-type bindings.
            * pt.cc (tsubst_aggr_type_1): Clear tf_qualifying_scope.  Tidy
            the function.
            (tsubst_decl) <case USING_DECL>: Set tf_qualifying_scope when
            substituting USING_DECL_SCOPE.
            (tsubst): Clear tf_qualifying_scope right away and remember if
            it was set.  Do the same for tf_tst_ok sooner.
            <case TYPENAME_TYPE>: Set tf_qualifying_scope when substituting
            TYPE_CONTEXT.  Pass tf_qualifying_scope to make_typename_type
            if it was set.
            (tsubst_qualified_id): Set tf_qualifying_scope when substituting
            the scope.
            (tsubst_copy): Clear tf_qualifying_scope and remember if it was
            set.
            <case SCOPE_REF>: Set tf_qualifying_scope when substituting the
            scope.
            <case *_TYPE>: Pass tf_qualifying_scope to tsubst if it was set.
            * search.cc (lookup_member): Document default argument.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/template/typename24.C: New test.
            * g++.dg/template/typename25.C: New test.
            * g++.dg/template/typename25a.C: New test.
            * g++.dg/template/typename26.C: New test.
Comment 4 Patrick Palka 2023-02-16 16:27:54 UTC
Should be fixed for GCC 13, thanks for the bug report.
Comment 5 Andrew Pinski 2025-10-07 17:19:00 UTC
*** Bug 122192 has been marked as a duplicate of this bug. ***