Bug 122752 - [15/16 Regression] Compilation error about class not being a non-union class type for valid C++ code
Summary: [15/16 Regression] Compilation error about class not being a non-union class ...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 16.0
: P1 normal
Target Milestone: 15.3
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
: 123590 (view as bug list)
Depends on:
Blocks:
 
Reported: 2025-11-18 20:32 UTC by Christoph
Modified: 2026-01-14 23:39 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work: 15.2.0
Known to fail: 15.2.1, 16.0
Last reconfirmed: 2025-11-18 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Christoph 2025-11-18 20:32:05 UTC
The recently version of Debian's g++ caused compilation errors in some packages
during rebuilds. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1120951

Same happens with latest 16.0 trunk version with Godbolt https://godbolt.org/z/xodooafea for this reduced C++ program:

#include <set>

template <class S>
class A
{
  typedef S VertexSet;

public:
  typename VertexSet::size_type size();

private:
  VertexSet vertices_;
};

template <class S>
inline typename A<S>::VertexSet::size_type A<S>::size()
{
  return vertices_.size();
}

int main()
{
  A<std::set<int>> a;
  return a.size();
}

with the error:

<source>:16:55: error: 'class A<S>::VertexSet' resolves to 'A<S>::VertexSet',
which is not a non-union class type [-Wtemplate-body]
   16 | inline typename A<S>::VertexSet::size_type A<S>::size()
      |                                                       ^
Compiler returned: 1

With older version of g++ 15 is compiled without an error.
Comment 1 Drea Pinski 2025-11-18 20:41:23 UTC
Reduced:
```
template <class S>
struct A
{
  typedef S c;
  typename S::n size();
};

template <class S>
inline typename A<S>::c::n A<S>::size()
{
  return 0;
}
```

Confirmed. This works for me with the GCC 15.2.0 release so either it is a bug introduced afterwards or the report to debian is wrong about the version # (though maybe debian version # is also wrong because it says 15.2.0).
Comment 2 Jakub Jelinek 2025-11-18 21:07:21 UTC
This is rejected since r16-4368-g49ddf362f0a7c1fdeb62f13a852a2fdec9d6fe6d
Comment 3 GCC Commits 2025-12-15 19:32:05 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:2bf057c53e97004034ed1528d850a214372305b8

commit r16-6141-g2bf057c53e97004034ed1528d850a214372305b8
Author: Patrick Palka <ppalka@redhat.com>
Date:   Mon Dec 15 14:30:53 2025 -0500

    c++: nested typename type resolving to wildcard type [PR122752]
    
    Here typename A<S>::VertexSet::size_type within the out-of-line
    declaration is initially parsed at namespace scope, so the LHS
    A<S>::VertexSet is treated as a dependent name and represented as a
    TYPENAME_TYPE with tag_type as class_type.[1]
    
    Once we realize we're parsing a member declarator we call
    maybe_update_decl_type to reprocess the TYPENAME_TYPE relative to the
    class template scope, during which make_typename_type succeeds in
    resolving the lookup and returns the member typedef VertexSet (to the
    TEMPLATE_TYPE_PARM S).  But then the caller tsubst complains that this
    result isn't a class type as per the tag_type.
    
    This patch just relaxes tsubst to allow TYPENAME_TYPE getting resolved
    to a wildcard type regardless of the tag_type.  This does mean we lose
    information about the tag_type during a subsequent tsubst, but that's
    probably harmless (famous last words).
    
    [1]: The tag_type should probably be scope_type.  Changing this seems
    to be a matter of changing cp_parser_qualifying_entity to pass
    scope_type instead of class_type, but I don't feel confident about that
    and it seems risky.  I then got confused as to why that function passes
    none_type in the !type_p case; to me it should use scope_type
    unconditionally, but doing so breaks things.  This approach seems safer
    to backport.
    
            PR c++/122752
    
    gcc/cp/ChangeLog:
    
            * pt.cc (tsubst) <case TYPENAME_TYPE>: Allow TYPENAME_TYPE
            resolving to another wildcard type.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/template/dependent-name19.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
Comment 4 GCC Commits 2025-12-16 15:33:57 UTC
The releases/gcc-15 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

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

commit r15-10611-gbb061a3b815249f2a4f375c3f7c6ea88978b9804
Author: Patrick Palka <ppalka@redhat.com>
Date:   Mon Dec 15 14:30:53 2025 -0500

    c++: nested typename type resolving to wildcard type [PR122752]
    
    Here typename A<S>::VertexSet::size_type within the out-of-line
    declaration is initially parsed at namespace scope, so the LHS
    A<S>::VertexSet is treated as a dependent name and represented as a
    TYPENAME_TYPE with tag_type as class_type.[1]
    
    Once we realize we're parsing a member declarator we call
    maybe_update_decl_type to reprocess the TYPENAME_TYPE relative to the
    class template scope, during which make_typename_type succeeds in
    resolving the lookup and returns the member typedef VertexSet (to the
    TEMPLATE_TYPE_PARM S).  But then the caller tsubst complains that this
    result isn't a class type as per the tag_type.
    
    This patch just relaxes tsubst to allow TYPENAME_TYPE getting resolved
    to a wildcard type regardless of the tag_type.  This does mean we lose
    information about the tag_type during a subsequent tsubst, but that's
    probably harmless (famous last words).
    
    [1]: The tag_type should probably be scope_type.  Changing this seems
    to be a matter of changing cp_parser_qualifying_entity to pass
    scope_type instead of class_type, but I don't feel confident about that
    and it seems risky.  I then got confused as to why that function passes
    none_type in the !type_p case; to me it should use scope_type
    unconditionally, but doing so breaks things.  This approach seems safer
    to backport.
    
            PR c++/122752
    
    gcc/cp/ChangeLog:
    
            * pt.cc (tsubst) <case TYPENAME_TYPE>: Allow TYPENAME_TYPE
            resolving to another wildcard type.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/template/dependent-name19.C: New test.
    
    Reviewed-by: Jason Merrill <jason@redhat.com>
    (cherry picked from commit 2bf057c53e97004034ed1528d850a214372305b8)
Comment 5 Patrick Palka 2025-12-16 15:39:23 UTC
Should be fixed for GCC 15.3, thanks for the bug report.
Comment 6 Drea Pinski 2026-01-14 23:39:52 UTC
*** Bug 123590 has been marked as a duplicate of this bug. ***