On Wed, Jul 17, 2024 at 10:55 AM Patrick Palka <ppa...@redhat.com> wrote:
>
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
> OK for trunk?

Just an FYI. This broke xalancbmk_r in SPEC 2017. clang has a flag to
delay the checking until instantiation time to work around this buggy
code, -fdelayed-template-parsing . Does it make sense to add a similar
one for GCC? See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116064
and https://github.com/llvm/llvm-project/issues/96859 for reference.

Thanks,
Andrew Pinski

>
> -- >8 --
>
> When the scope of a qualified name is the current instantiation, and
> qualified lookup finds nothing at template definition time, then we
> know it'll find nothing at instantiation time (unless the current
> instantiation has dependent bases).  So such qualified name lookup
> failure can be diagnosed ahead of time as per [temp.res.general]/6.
>
> This patch implements that, for qualified names of the form:
>
>   this->non_existent
>   a.non_existent
>   A::non_existent
>   typename A::non_existent
>
> It turns out we already optimistically attempt qualified lookup of
> basically every qualified name, even when it's dependently scoped, and
> just suppress issuing a lookup failure diagnostic after the fact when
> the scope is a dependent type.  So implementing this is mostly a
> matter of restricting the diagnostic suppression to "dependentish"
> scopes, rather than all dependently typed scopes.
>
> The cp_parser_conversion_function_id change is needed to avoid regressing
> lookup/using8.C:
>
>   using A<T>::operator typename A<T>::Nested*;
>
> When resolving A<T>::Nested we consider it not dependently scoped since
> we entered A<T> from cp_parser_conversion_function_id earlier.   But this
> A<T> is the implicit instantiation A<T> not the primary template type A<T>,
> and so the lookup of Nested fails which we now diagnose.  This patch works
> around this by not entering the template scope of a qualified conversion
> function-id in this case, i.e. if we're in an expression vs declaration
> context, by seeing if the type already went through finish_template_type
> with entering_scope=true.
>
> gcc/cp/ChangeLog:
>
>         * decl.cc (make_typename_type): Restrict name lookup failure
>         punting to dependentish_scope_p instead of dependent_type_p.
>         * error.cc (qualified_name_lookup_error): Improve diagnostic
>         when the scope is the current instantiation.
>         * parser.cc (cp_parser_diagnose_invalid_type_name): Likewise.
>         (cp_parser_conversion_function_id): Don't call push_scope on
>         a template scope unless we're in a declaration context.
>         (cp_parser_lookup_name): Restrict name lookup failure
>         punting to dependentish_scope_p instead of depedent_type_p.
>         * semantics.cc (finish_id_expression_1): Likewise.
>         * typeck.cc (finish_class_member_access_expr): Likewise.
>
> libstdc++-v3/ChangeLog:
>
>         * include/experimental/socket
>         (basic_socket_iostream::basic_socket_iostream): Fix typo.
>         * include/tr2/dynamic_bitset
>         (__dynamic_bitset_base::_M_is_proper_subset_of): Likewise.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/cpp0x/alignas18.C: Expect name lookup error for U::X.
>         * g++.dg/cpp0x/forw_enum13.C: Expect name lookup error for
>         D3::A and D4<T>::A.
>         * g++.dg/parse/access13.C: Declare A::E::V to avoid name lookup
>         failure and preserve intent of the test.
>         * g++.dg/parse/enum11.C: Expect extra errors, matching the
>         non-template case.
>         * g++.dg/template/crash123.C: Avoid name lookup failure to
>         preserve intent of the test.
>         * g++.dg/template/crash124.C: Likewise.
>         * g++.dg/template/crash7.C: Adjust expected diagnostics.
>         * g++.dg/template/dtor6.C: Declare A::~A() to avoid name lookup
>         failure and preserve intent of the test.
>         * g++.dg/template/error22.C: Adjust expected diagnostics.
>         * g++.dg/template/static30.C: Avoid name lookup failure to
>         preserve intent of the test.
>         * g++.old-deja/g++.other/decl5.C: Adjust expected diagnostics.
>         * g++.dg/template/non-dependent34.C: New test.
> ---
>  gcc/cp/decl.cc                                |  2 +-
>  gcc/cp/error.cc                               |  3 +-
>  gcc/cp/parser.cc                              | 10 +++--
>  gcc/cp/semantics.cc                           |  2 +-
>  gcc/cp/typeck.cc                              |  2 +-
>  gcc/testsuite/g++.dg/cpp0x/alignas18.C        |  3 +-
>  gcc/testsuite/g++.dg/cpp0x/forw_enum13.C      |  6 +--
>  gcc/testsuite/g++.dg/parse/access13.C         |  1 +
>  gcc/testsuite/g++.dg/parse/enum11.C           |  2 +-
>  gcc/testsuite/g++.dg/template/crash123.C      |  2 +-
>  gcc/testsuite/g++.dg/template/crash124.C      |  4 +-
>  gcc/testsuite/g++.dg/template/crash7.C        |  6 +--
>  gcc/testsuite/g++.dg/template/dtor6.C         |  3 +-
>  gcc/testsuite/g++.dg/template/error22.C       |  2 +-
>  .../g++.dg/template/non-dependent34.C         | 44 +++++++++++++++++++
>  gcc/testsuite/g++.dg/template/static30.C      |  4 +-
>  gcc/testsuite/g++.old-deja/g++.other/decl5.C  |  2 +-
>  libstdc++-v3/include/experimental/socket      |  2 +-
>  libstdc++-v3/include/tr2/dynamic_bitset       |  2 +-
>  19 files changed, 75 insertions(+), 27 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/template/non-dependent34.C
>
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index e7bb4fa3089..3c5ad554ff2 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -4536,7 +4536,7 @@ make_typename_type (tree context, tree name, enum 
> tag_types tag_type,
>    else
>      t = NULL_TREE;
>
> -  if ((!t || TREE_CODE (t) == TREE_LIST) && dependent_type_p (context))
> +  if ((!t || TREE_CODE (t) == TREE_LIST) && dependentish_scope_p (context))
>      return build_typename_type (context, name, fullname, tag_type);
>
>    want_template = TREE_CODE (fullname) == TEMPLATE_ID_EXPR;
> diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> index e35448f5434..6d99cb27703 100644
> --- a/gcc/cp/error.cc
> +++ b/gcc/cp/error.cc
> @@ -4714,7 +4714,8 @@ qualified_name_lookup_error (tree scope, tree name,
>      ; /* We already complained.  */
>    else if (TYPE_P (scope))
>      {
> -      if (!COMPLETE_TYPE_P (scope))
> +      if (!COMPLETE_TYPE_P (scope)
> +         && !currently_open_class (scope))
>         error_at (location, "incomplete type %qT used in nested name 
> specifier",
>                   scope);
>        else if (TREE_CODE (decl) == TREE_LIST)
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 1dd0efaf963..efd5d6f29a7 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -3888,7 +3888,8 @@ cp_parser_diagnose_invalid_type_name (cp_parser 
> *parser, tree id,
>        else if (TYPE_P (parser->scope))
>         {
>           auto_diagnostic_group d;
> -         if (!COMPLETE_TYPE_P (parser->scope))
> +         if (!COMPLETE_TYPE_P (parser->scope)
> +             && !currently_open_class (parser->scope))
>             cxx_incomplete_type_error (location_of (id), NULL_TREE,
>                                        parser->scope);
>           else if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
> @@ -17514,7 +17515,10 @@ cp_parser_conversion_function_id (cp_parser* parser)
>
>       In order to see that `I' is a type-name in the definition, we
>       must be in the scope of `S'.  */
> -  if (saved_scope)
> +  if (saved_scope
> +      /* In A<T>::operator I(), we don't want to enter A<T> if we're
> +        in an expression rather than declaration context.  */
> +      && adjust_type_for_entering_scope (saved_scope) == saved_scope)
>      pushed_scope = push_scope (saved_scope);
>    /* Parse the conversion-type-id.  */
>    type = cp_parser_conversion_type_id (parser);
> @@ -32219,7 +32223,7 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
>        /* If the scope is a dependent type and either we deferred lookup or
>          we did lookup but didn't find the name, rememeber the name.  */
>        if (decl == error_mark_node && TYPE_P (parser->scope)
> -         && dependent_type_p (parser->scope))
> +         && dependentish_scope_p (parser->scope))
>         {
>           if (tag_type)
>             {
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index cd3df13772d..c21572e5d7f 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -4353,7 +4353,7 @@ finish_id_expression_1 (tree id_expression,
>           /* Name lookup failed.  */
>           if (scope
>               && (!TYPE_P (scope)
> -                 || (!dependent_type_p (scope)
> +                 || (!dependentish_scope_p (scope)
>                       && !(identifier_p (id_expression)
>                            && IDENTIFIER_CONV_OP_P (id_expression)
>                            && dependent_type_p (TREE_TYPE (id_expression))))))
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index 5041a70d089..e5361a5e693 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -3542,7 +3542,7 @@ finish_class_member_access_expr (cp_expr object, tree 
> name, bool template_p,
>           afi.maybe_suggest_accessor (TYPE_READONLY (object_type));
>           if (member == NULL_TREE)
>             {
> -             if (dependent_type_p (object_type))
> +             if (dependentish_scope_p (object_type))
>                 /* Try again at instantiation time.  */
>                 goto dependent;
>               if (complain & tf_error)
> diff --git a/gcc/testsuite/g++.dg/cpp0x/alignas18.C 
> b/gcc/testsuite/g++.dg/cpp0x/alignas18.C
> index 820bdd2d7ca..9c25cd0942b 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/alignas18.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/alignas18.C
> @@ -3,6 +3,5 @@
>
>  template <typename T> struct S {
>    using U = S;
> -  // FIXME: This is ill-formed; see PR90847.
> -  void fn() alignas(U::X);
> +  void fn() alignas(U::X); // { dg-error "not a member" }
>  };
> diff --git a/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C 
> b/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C
> index b8027f0c28d..37ffad9b956 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/forw_enum13.C
> @@ -18,13 +18,13 @@ class D2
>  template <typename T>
>  class D3
>  {
> -  enum D3::A { foo } c; // { dg-error "extra qualification not allowed" }
> +  enum D3::A { foo } c; // { dg-error "does not name an enumeration" }
>  };
>
>  template <typename T>
>  class D4
>  {
> -  enum D4<T>::A { foo } c; // { dg-error "extra qualification not allowed" }
> +  enum D4<T>::A { foo } c; // { dg-error "does not name an enumeration" }
>  };
>
>  template <typename T>
> @@ -32,7 +32,7 @@ class D5
>  {
>    class D6
>    {
> -    enum D6::A { foo } c; // { dg-error "extra qualification not allowed" }
> +    enum D6::A { foo } c; // { dg-error "does not name an enumeration" }
>    };
>  };
>
> diff --git a/gcc/testsuite/g++.dg/parse/access13.C 
> b/gcc/testsuite/g++.dg/parse/access13.C
> index 41463c5dde5..ea3cf1111a8 100644
> --- a/gcc/testsuite/g++.dg/parse/access13.C
> +++ b/gcc/testsuite/g++.dg/parse/access13.C
> @@ -2,6 +2,7 @@
>
>  template <typename> struct A
>  {
> +  struct E { static int V; };
>    A::E::V;                    // { dg-warning "access decl" }
>    enum { V };                 // { dg-error "conflicts with a previous decl" 
> }
>  };
> diff --git a/gcc/testsuite/g++.dg/parse/enum11.C 
> b/gcc/testsuite/g++.dg/parse/enum11.C
> index 68ddedbeeec..8bab16a6799 100644
> --- a/gcc/testsuite/g++.dg/parse/enum11.C
> +++ b/gcc/testsuite/g++.dg/parse/enum11.C
> @@ -2,5 +2,5 @@
>
>  template<typename> struct A
>  {
> -  enum A::B::C {};   // { dg-error "has not been declared" }
> +  enum A::B::C {};   // { dg-error "" }
>  };
> diff --git a/gcc/testsuite/g++.dg/template/crash123.C 
> b/gcc/testsuite/g++.dg/template/crash123.C
> index 20a49619c6f..20a71cf192b 100644
> --- a/gcc/testsuite/g++.dg/template/crash123.C
> +++ b/gcc/testsuite/g++.dg/template/crash123.C
> @@ -4,7 +4,7 @@ template <bool> struct VI {};
>  template <typename T>
>  struct IP
>  {
> -  static const bool r = IP<T>::r;  // { dg-error "depth" }
> +  static const bool r = IP<T*>::r;  // { dg-error "depth" }
>  };
>  template <typename T> struct V
>  {
> diff --git a/gcc/testsuite/g++.dg/template/crash124.C 
> b/gcc/testsuite/g++.dg/template/crash124.C
> index 4931aa8e9c6..5788ead4630 100644
> --- a/gcc/testsuite/g++.dg/template/crash124.C
> +++ b/gcc/testsuite/g++.dg/template/crash124.C
> @@ -4,12 +4,12 @@ template <bool> struct VI {};
>  template <typename T>
>  struct IP
>  {
> -  static const bool r = IP<T>::r;  // { dg-error "depth" }
> +  static const bool r = IP<T*>::r;  // { dg-error "depth" }
>  };
>  template <typename T>
>  struct V
>  {
> -  static const bool r = IP<T>::r;
> +  static const bool r = IP<T*>::r;
>    VI<r> vi;
>  };
>  struct X;
> diff --git a/gcc/testsuite/g++.dg/template/crash7.C 
> b/gcc/testsuite/g++.dg/template/crash7.C
> index 691628e7878..977b4e454ba 100644
> --- a/gcc/testsuite/g++.dg/template/crash7.C
> +++ b/gcc/testsuite/g++.dg/template/crash7.C
> @@ -7,9 +7,7 @@
>
>  template <typename> struct A
>  {
> -    template <typename> A(typename A::X) {} // { dg-error "incomplete" }
> +    template <typename> A(typename A::X) {} // { dg-error "does not name a 
> type" }
>  };
>
> -// We currently don't give the "no match" error because we don't add the
> -// invalid constructor template to TYPE_METHODS.
> -A<void> a;                     // { dg-message "required" }
> +A<void> a;                     // { dg-error "no match" }
> diff --git a/gcc/testsuite/g++.dg/template/dtor6.C 
> b/gcc/testsuite/g++.dg/template/dtor6.C
> index a3d778a1ea1..5757028814e 100644
> --- a/gcc/testsuite/g++.dg/template/dtor6.C
> +++ b/gcc/testsuite/g++.dg/template/dtor6.C
> @@ -1,8 +1,9 @@
>  // PR c++/40139
>
> -template<int> struct A
> +template<int N> struct A
>  {
>    static int i;
> +  ~A();
>  };
>
>  template<int N> int A<N>::i = { A::~A }; // { dg-error "36:invalid use of 
> non-static member function" }
> diff --git a/gcc/testsuite/g++.dg/template/error22.C 
> b/gcc/testsuite/g++.dg/template/error22.C
> index a7e61721113..af87992219b 100644
> --- a/gcc/testsuite/g++.dg/template/error22.C
> +++ b/gcc/testsuite/g++.dg/template/error22.C
> @@ -4,6 +4,6 @@ struct A
>  {
>      template<void (A::*)()> struct B {};
>      void ::foo(); // { dg-error "10:invalid use" }
> -    B<&A::foo> b; // { dg-error "incomplete type|template argument" }
> +    B<&A::foo> b; // { dg-error "'foo' is not a member of 'A'|template 
> argument" }
>  };
>
> diff --git a/gcc/testsuite/g++.dg/template/non-dependent34.C 
> b/gcc/testsuite/g++.dg/template/non-dependent34.C
> new file mode 100644
> index 00000000000..ed4c146ea3d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/non-dependent34.C
> @@ -0,0 +1,44 @@
> +// Verify we diagnose failed qualified lookup into the current
> +// instantiation ahead of time.
> +
> +namespace without_dependent_base {
> +template<class T>
> +struct A {
> +  void f(A& other) {
> +    A::x; // { dg-error "'x' is not a member" }
> +    this->x; // { dg-error "no member named 'x' }
> +    other.y; // { dg-error "no member named 'y' }
> +    typename A::type z; // { dg-error "does not name a type" }
> +
> +    struct B {
> +      void g(A& other) {
> +       A::x; // { dg-error "'x' is not a member" }
> +       this->x; // { dg-error "no member named 'x' }
> +       other.y; // { dg-error "no member named 'y' }
> +       typename A::type z; // { dg-error "does not name a type" }
> +      }
> +    };
> +  }
> +};
> +}
> +
> +namespace with_dependent_base {
> +template<class T>
> +struct A : T {
> +  void f(A& other) {
> +    A::x;
> +    this->x;
> +    other.y;
> +    typename A::type z;
> +
> +    struct B : T {
> +      void g(A& other) {
> +       A::x;
> +       this->x;
> +       other.y;
> +       typename A::type z;
> +      }
> +    };
> +  }
> +};
> +}
> diff --git a/gcc/testsuite/g++.dg/template/static30.C 
> b/gcc/testsuite/g++.dg/template/static30.C
> index 07dafe23ffa..248f9e9025e 100644
> --- a/gcc/testsuite/g++.dg/template/static30.C
> +++ b/gcc/testsuite/g++.dg/template/static30.C
> @@ -6,5 +6,5 @@ template <int> struct A
>    static const int i2;
>  };
>
> -template <int N> const int A<N>::i1(A<N>::i);
> -template <int N> const int A<N>::i2(3, A<N>::i); // { dg-error "expression 
> list" }
> +template <int N> const int A<N>::i1(A<N>::i1);
> +template <int N> const int A<N>::i2(3, A<N>::i2); // { dg-error "expression 
> list" }
> diff --git a/gcc/testsuite/g++.old-deja/g++.other/decl5.C 
> b/gcc/testsuite/g++.old-deja/g++.other/decl5.C
> index 26556aaa7ef..c24957f8bbe 100644
> --- a/gcc/testsuite/g++.old-deja/g++.other/decl5.C
> +++ b/gcc/testsuite/g++.old-deja/g++.other/decl5.C
> @@ -18,7 +18,7 @@ struct A {
>    struct Z;
>    expand me;          // { dg-error "'expand' does not name a type" }
>    void foo(struct A::e);
> -  void foo(struct A::z);  // { dg-error "incomplete" }
> +  void foo(struct A::z);  // { dg-error "does not name a type" }
>  };
>
>  struct Q;
> diff --git a/libstdc++-v3/include/experimental/socket 
> b/libstdc++-v3/include/experimental/socket
> index 02c27d66c6a..3fe83a001e6 100644
> --- a/libstdc++-v3/include/experimental/socket
> +++ b/libstdc++-v3/include/experimental/socket
> @@ -2450,7 +2450,7 @@ inline namespace v1
>         // XXX ???     ^^^^^^^
>        {
>         // XXX ??? this->init(std::addressof(_M_sb));
> -       this->set_rbduf(std::addressof(_M_sb));
> +       this->set_rdbuf(std::addressof(_M_sb));
>        }
>
>        template<typename... _Args>
> diff --git a/libstdc++-v3/include/tr2/dynamic_bitset 
> b/libstdc++-v3/include/tr2/dynamic_bitset
> index 274c4f6a138..f0878d7429e 100644
> --- a/libstdc++-v3/include/tr2/dynamic_bitset
> +++ b/libstdc++-v3/include/tr2/dynamic_bitset
> @@ -304,7 +304,7 @@ namespace tr2
>        bool
>        _M_is_proper_subset_of(const __dynamic_bitset_base& __b) const noexcept
>        {
> -       if (this->is_subset_of(__b))
> +       if (this->_M_is_subset_of(__b))
>           {
>             if (*this == __b)
>               return false;
> --
> 2.46.0.rc0.75.g04f5a52757
>

Reply via email to