I noticed that after the static_assert failures in lwg3466.cc, we got various follow-on errors because we went ahead and tried to instantiate the promise<T> member functions even after instantiating the class itself ran into problems. Interrupting instantiation of the class itself seems likely to cause error-recovery problems, but preventing instantiation of member functions seems strictly better for error-recovery.
This doesn't fix any of the specific testcases in PR96286, but addresses part of that problem space. Tested x86_64-pc-linux-gnu, applying to trunk. PR c++/96286 gcc/cp/ChangeLog: * cp-tree.h (struct lang_type): Add erroneous bit-field. (CLASSTYPE_ERRONEOUS): New. * pt.c (limit_bad_template_recursion): Check it. (instantiate_class_template_1): Set it. libstdc++-v3/ChangeLog: * testsuite/30_threads/promise/requirements/lwg3466.cc: Remove dg-prune-outputs. gcc/testsuite/ChangeLog: * g++.dg/template/access2.C: Split struct A. --- gcc/cp/cp-tree.h | 7 ++++++- gcc/cp/pt.c | 14 +++++++++++++- gcc/testsuite/g++.dg/template/access2.C | 6 +++++- .../30_threads/promise/requirements/lwg3466.cc | 4 ---- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 6a179375a56..ce7ca53a113 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2336,6 +2336,7 @@ struct GTY(()) lang_type { unsigned has_constexpr_ctor : 1; unsigned unique_obj_representations : 1; unsigned unique_obj_representations_set : 1; + bool erroneous : 1; /* When adding a flag here, consider whether or not it ought to apply to a template instance if it applies to the template. If @@ -2344,7 +2345,7 @@ struct GTY(()) lang_type { /* There are some bits left to fill out a 32-bit word. Keep track of this by updating the size of this bitfield whenever you add or remove a flag. */ - unsigned dummy : 5; + unsigned dummy : 4; tree primary_base; vec<tree_pair_s, va_gc> *vcall_indices; @@ -2660,6 +2661,10 @@ struct GTY(()) lang_type { /* Nonzero if a _DECL node requires us to output debug info for this class. */ #define CLASSTYPE_DEBUG_REQUESTED(NODE) \ (LANG_TYPE_CLASS_CHECK (NODE)->debug_requested) + +/* True if we saw errors while instantiating this class. */ +#define CLASSTYPE_ERRONEOUS(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->erroneous) /* Additional macros for inheritance information. */ diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index d7d0dce691c..fcf3ac31b25 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -10885,9 +10885,14 @@ limit_bad_template_recursion (tree decl) { struct tinst_level *lev = current_tinst_level; int errs = errorcount + sorrycount; - if (lev == NULL || errs == 0 || !neglectable_inst_p (decl)) + if (errs == 0 || !neglectable_inst_p (decl)) return false; + /* Avoid instantiating members of an ill-formed class. */ + if (DECL_CLASS_SCOPE_P (decl) + && CLASSTYPE_ERRONEOUS (DECL_CONTEXT (decl))) + return true; + for (; lev; lev = lev->next) if (neglectable_inst_p (lev->maybe_get_node ())) break; @@ -12212,6 +12217,13 @@ instantiate_class_template_1 (tree type) finish_struct_1 (type); TYPE_BEING_DEFINED (type) = 0; + /* Remember if instantiating this class ran into errors, so we can avoid + instantiating member functions in limit_bad_template_recursion. We set + this flag even if the problem was in another instantiation triggered by + this one, as that will likely also cause trouble for member functions. */ + if (errorcount + sorrycount > current_tinst_level->errors) + CLASSTYPE_ERRONEOUS (type) = true; + /* We don't instantiate default arguments for member functions. 14.7.1: The implicit instantiation of a class template specialization causes diff --git a/gcc/testsuite/g++.dg/template/access2.C b/gcc/testsuite/g++.dg/template/access2.C index 0620c10f79d..4a80bb4d280 100644 --- a/gcc/testsuite/g++.dg/template/access2.C +++ b/gcc/testsuite/g++.dg/template/access2.C @@ -5,6 +5,9 @@ template <class T> struct A { typename T::X x; // { dg-error "this context" } +}; + +template <class T> struct A2 { int f() { return T::i; } // { dg-error "this context" } }; @@ -16,5 +19,6 @@ class B { int main() { A<B> ab; // { dg-message "required" } - ab.f(); // { dg-message "required" } + A2<B> a2b; + a2b.f(); // { dg-message "required" } } diff --git a/libstdc++-v3/testsuite/30_threads/promise/requirements/lwg3466.cc b/libstdc++-v3/testsuite/30_threads/promise/requirements/lwg3466.cc index bb04bf0737c..acef47f12f3 100644 --- a/libstdc++-v3/testsuite/30_threads/promise/requirements/lwg3466.cc +++ b/libstdc++-v3/testsuite/30_threads/promise/requirements/lwg3466.cc @@ -27,17 +27,13 @@ std::promise<int(&)()> good2; std::promise<int[1]> bad; // { dg-error "here" } // { dg-error "result type must not be an array" "" { target *-*-* } 0 } -// { dg-prune-output {request for member '~int \[1\]'} } std::promise<int()> bad2; // { dg-error "here" } // { dg-error "result type must not be a function" "" { target *-*-* } 0 } -// { dg-prune-output {'sizeof \(int\(\)\)'} } struct Indestructible { ~Indestructible() = delete; }; std::promise<Indestructible> bad3; // { dg-error "here" } // { dg-error "result type must be destructible" "" { target *-*-* } 0 } -// { dg-prune-output {deleted function} } class PrivateDtor { public: PrivateDtor(); private: ~PrivateDtor(); }; std::promise<PrivateDtor> bad4; // { dg-error "here" } -// { dg-prune-output {is private} } base-commit: 729f6881cfcc6df3c15a1dd4ebd45bc46bb8f3e9 -- 2.27.0