* C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
@ 2018-12-19 20:27 Marek Polacek
2019-01-04 14:45 ` Marek Polacek
2019-01-07 15:44 ` Jason Merrill
0 siblings, 2 replies; 14+ messages in thread
From: Marek Polacek @ 2018-12-19 20:27 UTC (permalink / raw)
To: GCC Patches, Jason Merrill
Prompted by Jon's observation in 52869, I noticed that we don't treat
a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
As with member function bodies, default arguments, and NSDMIs, names used in
a noexcept-specifier of a member-function can be declared later in the class
body, so we need to wait and parse them at the end of the class.
For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
This wasn't as easy as I'd anticipated, because I needed to make sure to
* handle well accessing function parameters in the noexcept-specifier,
hence the maybe_{begin,end}_member_function_processing business,
* not regress diagnostic. See e.g. noexcept38.C for detecting "looser
throw specifier", or noexcept39.C, friend decls and redeclaration.
This is handled by functions like noexcept_override_late_checks and
check_redeclaration_exception_specification. I hope that's it.
Compiling libstdc++ was a fairly good stress test, and I've added a bunch
of reduced testcases I've collected along the way.
I also noticed we're not properly detecting using 'this' in static member
functions; tracked in 88548.
Bootstrapped/regtested on x86_64-linux, ok for trunk?
2018-12-19 Marek Polacek <polacek@redhat.com>
PR c++/86476 - noexcept-specifier is a complete-class context.
PR c++/52869
* cp-tree.def (DEFAULT_ARG): Update commentary.
* cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro.
(check_redeclaration_exception_specification): Declare.
(maybe_check_throw_specifier): Declare.
* decl.c (check_redeclaration_exception_specification): No longer
static. Handle UNPARSED_NOEXCEPT_SPEC_P.
* except.c (nothrow_spec_p): Accept DEFAULT_ARG in assert.
* parser.c (cp_parser_noexcept_specification_opt,
cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
Forward-declare.
(unparsed_noexcepts): New macro.
(push_unparsed_function_queues): Update initializer.
(cp_parser_init_declarator): Maybe save the noexcept-specifier to
post process.
(maybe_begin_member_function_processing): New.
(maybe_end_member_function_processing): New.
(cp_parser_class_specifier_1): Implement delayed parsing of
noexcept-specifiers.
(cp_parser_member_declaration): Maybe save the noexcept-specifier to
post process.
(cp_parser_save_noexcept): New.
(cp_parser_late_noexcept_specifier): New.
(noexcept_override_late_checks): New.
(cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept
instead of the normal processing if needed.
(cp_parser_save_member_function_body): Maybe save the
noexcept-specifier to post process.
* parser.h (cp_unparsed_functions_entry): Add new field to carry
a noexcept-specifier.
* pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
* search.c (maybe_check_throw_specifier): New function, broken out
of...
(check_final_overrider): ...here. Call maybe_check_throw_specifier.
* tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
(cp_tree_equal): Handle DEFAULT_ARG.
* typeck2.c (merge_exception_specifiers): If an unparsed noexcept
expression has been passed, return it instead of merging it.
* g++.dg/cpp0x/noexcept34.C: New test.
* g++.dg/cpp0x/noexcept35.C: New test.
* g++.dg/cpp0x/noexcept36.C: New test.
* g++.dg/cpp0x/noexcept37.C: New test.
* g++.dg/cpp0x/noexcept38.C: New test.
* g++.dg/cpp0x/noexcept39.C: New test.
diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 43d90eb1efb..aa8b752d8f4 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
/* An un-parsed default argument. Holds a vector of input tokens and
a vector of places where the argument was instantiated before
- parsing had occurred. */
+ parsing had occurred. This is also used for delayed NSDMIs and
+ noexcept-specifier parsing. For a noexcept-specifier, the vector
+ holds a function declaration used for late checking. */
DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
/* An uninstantiated/unevaluated noexcept-specification. For the
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 1d806b782bd..bd3cd200fcb 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1193,6 +1193,9 @@ struct GTY (()) tree_default_arg {
#define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
(DEFERRED_NOEXCEPT_SPEC_P (NODE) \
&& DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
+#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
+ ((NODE) && (TREE_PURPOSE (NODE)) \
+ && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
struct GTY (()) tree_deferred_noexcept {
struct tree_base base;
@@ -6430,6 +6433,8 @@ extern bool check_array_designated_initializer (constructor_elt *,
unsigned HOST_WIDE_INT);
extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
extern tree build_explicit_specifier (tree, tsubst_flags_t);
+extern void check_redeclaration_exception_specification
+ (tree, tree);
/* in decl2.c */
extern void record_mangling (tree, bool);
@@ -6894,6 +6899,7 @@ extern tree copied_binfo (tree, tree);
extern tree original_binfo (tree, tree);
extern int shared_member_p (tree);
extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
+extern bool maybe_check_throw_specifier (tree, tree);
/* The representation of a deferred access check. */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index d6028e3608c..74444db8623 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -1140,7 +1140,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
function templates. If their exception specifications do not
match, issue a diagnostic. */
-static void
+void
check_redeclaration_exception_specification (tree new_decl,
tree old_decl)
{
@@ -1152,6 +1152,16 @@ check_redeclaration_exception_specification (tree new_decl,
&& UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
return;
+ /* We can't compare unparsed noexcept-specifiers. Save the old decl
+ and check this again after we've parsed the noexcept-specifiers
+ for real. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
+ {
+ vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
+ copy_decl (old_decl));
+ return;
+ }
+
if (!type_dependent_expression_p (old_decl))
{
maybe_instantiate_noexcept (new_decl);
diff --git gcc/cp/except.c gcc/cp/except.c
index b04eb0c5332..92fc39d3968 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
|| TREE_VALUE (spec)
|| spec == noexcept_false_spec
|| TREE_PURPOSE (spec) == error_mark_node
+ || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
|| processing_template_decl);
return false;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index b57e35d04c5..267b3518156 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
static cp_token_cache *cp_token_cache_new
(cp_token *, cp_token *);
+static tree cp_parser_noexcept_specification_opt
+ (cp_parser *, bool, bool *, bool);
+static tree cp_parser_late_noexcept_specifier
+ (cp_parser *, tree);
+static void noexcept_override_late_checks
+ (tree, tree);
static void cp_parser_initial_pragma
(cp_token *);
@@ -1968,11 +1974,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_classes \
parser->unparsed_queues->last ().classes
+#define unparsed_noexcepts \
+ parser->unparsed_queues->last ().noexcepts
static void
push_unparsed_function_queues (cp_parser *parser)
{
- cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
+ cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
+ NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -20315,7 +20324,13 @@ cp_parser_init_declarator (cp_parser* parser,
/*asmspec=*/NULL_TREE,
attr_chainon (attributes, prefix_attributes));
if (decl && TREE_CODE (decl) == FUNCTION_DECL)
- cp_parser_save_default_args (parser, decl);
+ {
+ cp_parser_save_default_args (parser, decl);
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
+ }
cp_finalize_omp_declare_simd (parser, decl);
cp_finalize_oacc_routine (parser, decl, false);
}
@@ -23058,6 +23073,45 @@ cp_parser_class_name (cp_parser *parser,
return decl;
}
+/* Make sure that any member-function parameters are in scope.
+ For instance, a function's noexcept-specifier can use the function's
+ parameters:
+
+ struct S {
+ void fn (int p) noexcept(noexcept(p));
+ };
+
+ so we need to make sure name lookup can find them. This is used
+ when we delay parsing of the noexcept-specifier. */
+
+static void
+maybe_begin_member_function_processing (tree decl)
+{
+ begin_scope (sk_function_parms, decl);
+ tree args = DECL_ARGUMENTS (decl);
+ args = nreverse (args);
+
+ tree next;
+ for (tree parm = args; parm; parm = next)
+ {
+ next = DECL_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ pushdecl (parm);
+ }
+ /* Get the decls in their original chain order and record in the
+ function. This is all and only the PARM_DECLs that were
+ pushed into scope by the loop above. */
+ DECL_ARGUMENTS (decl) = get_local_decls ();
+}
+
+/* Undo the effects of maybe_begin_member_function_processing. */
+
+static void
+maybe_end_member_function_processing (void)
+{
+ pop_bindings_and_leave_scope ();
+}
+
/* Parse a class-specifier.
class-specifier:
@@ -23368,6 +23422,56 @@ cp_parser_class_specifier_1 (cp_parser* parser)
vec_safe_truncate (unparsed_classes, 0);
after_nsdmi_defaulted_late_checks (type);
+ /* If there are noexcept-specifiers that have not yet been processed,
+ take care of them now. */
+ class_type = NULL_TREE;
+ pushed_scope = NULL_TREE;
+ FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+ {
+ if (class_type != DECL_CONTEXT (decl))
+ {
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+ class_type = DECL_CONTEXT (decl);
+ pushed_scope = push_scope (class_type);
+ }
+
+ /* Make sure that any template parameters are in scope. */
+ maybe_begin_member_template_processing (decl);
+
+ /* Make sure that any member-function parameters are in scope. */
+ maybe_begin_member_function_processing (decl);
+
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ spec = TREE_PURPOSE (spec);
+ vec<tree, va_gc> *p = DEFARG_INSTANTIATIONS (spec);
+ tree old_decl = (p ? (*p)[0] : NULL_TREE);
+
+ /* Now we can parse the noexcept-specifier. */
+ spec = cp_parser_late_noexcept_specifier (parser, spec);
+ if (spec != error_mark_node)
+ TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
+
+ /* If we've stashed an old declaration, it means we need to
+ perform late redeclaration checking. */
+ if (old_decl)
+ check_redeclaration_exception_specification (decl, old_decl);
+
+ /* The finish_struct call above performed various override checking,
+ but it skipped unparsed noexcept-specifier operands. Now that we
+ have resolved them, check again. */
+ noexcept_override_late_checks (type, decl);
+
+ /* Remove any member-function parameters from the symbol table. */
+ maybe_end_member_function_processing ();
+
+ /* Remove any template parameters from the symbol table. */
+ maybe_end_member_template_processing ();
+ }
+ vec_safe_truncate (unparsed_noexcepts, 0);
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+
/* Now parse the body of the functions. */
if (flag_openmp)
{
@@ -24537,6 +24641,12 @@ cp_parser_member_declaration (cp_parser* parser)
else
decl = finish_fully_implicit_template (parser, decl);
}
+ if (decl && TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
+ }
}
cp_finalize_omp_declare_simd (parser, decl);
@@ -24919,6 +25029,89 @@ cp_parser_base_specifier (cp_parser* parser)
/* Exception handling [gram.exception] */
+/* Save the tokens that make up the noexcept-specifier for a member-function.
+ Returns a DEFAULT_ARG. */
+
+static tree
+cp_parser_save_noexcept (cp_parser *parser)
+{
+ cp_token *first = parser->lexer->next_token;
+ /* We want everything up to, including, the final ')'. */
+ cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
+ cp_token *last = parser->lexer->next_token;
+
+ /* As with default arguments and NSDMIs, make us of DEFAULT_ARG
+ to carry the information we will need. */
+ tree expr = make_node (DEFAULT_ARG);
+ /* Save away the noexcept-specifier; we will process it when the
+ class is complete. */
+ DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
+ DEFARG_INSTANTIATIONS (expr) = NULL;
+ expr = build_tree_list (expr, NULL_TREE);
+ return expr;
+}
+
+/* Used for late processing of noexcept-specifiers of member-functions.
+ DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
+ we saved for later; parse it now. */
+
+static tree
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+{
+ /* Make sure we've gotten something that hasn't been parsed yet. */
+ gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
+
+ push_unparsed_function_queues (parser);
+
+ /* Push the saved tokens for the noexcept-specifier onto the parser's
+ lexer stack. */
+ cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
+ cp_parser_push_lexer_for_tokens (parser, tokens);
+
+ /* Parse the cached noexcept-specifier. */
+ tree parsed_arg
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ NULL,
+ /*return_cond=*/false);
+
+ /* Revert to the main lexer. */
+ cp_parser_pop_lexer (parser);
+
+ /* Restore the queue. */
+ pop_unparsed_function_queues (parser);
+
+ /* And we're done. */
+ return parsed_arg;
+}
+
+/* Perform late checking of overriding function with respect to their
+ noexcept-specifiers. TYPE is the class and FNDECL is the function
+ that potentially overrides some virtual function with the same
+ signature. */
+
+static void
+noexcept_override_late_checks (tree type, tree fndecl)
+{
+ tree binfo = TYPE_BINFO (type);
+ tree base_binfo;
+
+ if (DECL_STATIC_FUNCTION_P (fndecl))
+ return;
+
+ for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+ {
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ if (!TYPE_POLYMORPHIC_P (basetype))
+ continue;
+
+ tree fn = look_for_overrides_here (basetype, fndecl);
+ if (fn)
+ maybe_check_throw_specifier (fndecl, fn);
+ }
+}
+
/* Parse an (optional) noexcept-specification.
noexcept-specification:
@@ -24947,6 +25140,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
if (cp_parser_is_keyword (token, RID_NOEXCEPT))
{
tree expr;
+
+ /* [class.mem]/6 says that a noexcept-specifer (within the
+ member-specification of the class) is a complete-class context of
+ a class. So, if the noexcept-specifier has the optional expression,
+ just save the tokens, and reparse this after we're done with the
+ class. */
+ if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
+ && at_class_scope_p ()
+ && TYPE_BEING_DEFINED (current_class_type)
+ && !LAMBDA_TYPE_P (current_class_type))
+ return cp_parser_save_noexcept (parser);
+
cp_lexer_consume_token (parser->lexer);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
@@ -28120,7 +28325,12 @@ cp_parser_save_member_function_body (cp_parser* parser,
return error_mark_node;
}
- /* Remember it, if there default args to post process. */
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, fn);
+
+ /* Remember it, if there are default args to post process. */
cp_parser_save_default_args (parser, fn);
/* Save away the tokens that make up the body of the
diff --git gcc/cp/parser.h gcc/cp/parser.h
index 8bfa3f3b9c4..df0c6c1960e 100644
--- gcc/cp/parser.h
+++ gcc/cp/parser.h
@@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Nested classes go in this vector, so that we can do some final
processing after parsing any NSDMIs. */
vec<tree, va_gc> *classes;
+
+ /* Functions with noexcept-specifiers that require post-processing. */
+ vec<tree, va_gc> *noexcepts;
};
diff --git gcc/cp/pt.c gcc/cp/pt.c
index e99de71ea9e..382a14f7d6a 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -24988,8 +24988,9 @@ dependent_type_p_r (tree type)
if (tree noex = TREE_PURPOSE (spec))
/* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
affect overload resolution and treating it as dependent breaks
- things. */
+ things. Same for an unparsed noexcept expression. */
if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
+ && TREE_CODE (noex) != DEFAULT_ARG
&& value_dependent_expression_p (noex))
return true;
return false;
diff --git gcc/cp/search.c gcc/cp/search.c
index d700fe328f4..3e6494dd98f 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -1850,6 +1850,38 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
NULL, &lfd);
}
+/* Check throw specifier of OVERRIDER is at least as strict as
+ the one of BASEFN. */
+
+bool
+maybe_check_throw_specifier (tree overrider, tree basefn)
+{
+ maybe_instantiate_noexcept (basefn);
+ maybe_instantiate_noexcept (overrider);
+ tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+ tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
+ if (DECL_INVALID_OVERRIDER_P (overrider))
+ return true;
+
+ /* Can't check this yet. Pretend this is fine and let
+ noexcept_override_late_checks check this later. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
+ || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
+ return true;
+
+ if (!comp_except_specs (base_throw, over_throw, ce_derived))
+ {
+ auto_diagnostic_group d;
+ error ("looser throw specifier for %q+#F", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %q#F", basefn);
+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
+ return false;
+ }
+ return true;
+}
+
/* Check that virtual overrider OVERRIDER is acceptable for base function
BASEFN. Issue diagnostic, and return zero, if unacceptable. */
@@ -1860,7 +1892,6 @@ check_final_overrider (tree overrider, tree basefn)
tree base_type = TREE_TYPE (basefn);
tree over_return = fndecl_declared_return_type (overrider);
tree base_return = fndecl_declared_return_type (basefn);
- tree over_throw, base_throw;
int fail = 0;
@@ -1944,21 +1975,8 @@ check_final_overrider (tree overrider, tree basefn)
return 0;
}
- /* Check throw specifier is at least as strict. */
- maybe_instantiate_noexcept (basefn);
- maybe_instantiate_noexcept (overrider);
- base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
- over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
-
- if (!comp_except_specs (base_throw, over_throw, ce_derived))
- {
- auto_diagnostic_group d;
- error ("looser throw specifier for %q+#F", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#F", basefn);
- DECL_INVALID_OVERRIDER_P (overrider) = 1;
- return 0;
- }
+ if (!maybe_check_throw_specifier (overrider, basefn))
+ return 0;
/* Check for conflicting type attributes. But leave transaction_safe for
set_one_vmethod_tm_attributes. */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 97074dfab56..a7d1e58c73a 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2542,6 +2542,7 @@ canonical_eh_spec (tree raises)
if (raises == NULL_TREE)
return raises;
else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+ || UNPARSED_NOEXCEPT_SPEC_P (raises)
|| uses_template_parms (raises)
|| uses_template_parms (TREE_PURPOSE (raises)))
/* Keep a dependent or deferred exception specification. */
@@ -3653,6 +3654,7 @@ cp_tree_equal (tree t1, tree t2)
case TEMPLATE_DECL:
case IDENTIFIER_NODE:
case SSA_NAME:
+ case DEFAULT_ARG:
return false;
case BASELINK:
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 64e36efd17e..6fec77d8269 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -2302,6 +2302,12 @@ merge_exception_specifiers (tree list, tree add)
{
tree noex, orig_list;
+ /* We don't want to lose the unparsed operand lest we miss diagnostics. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (list))
+ return list;
+ else if (UNPARSED_NOEXCEPT_SPEC_P (add))
+ return add;
+
/* No exception-specifier or noexcept(false) are less strict than
anything else. Prefer the newer variant (LIST). */
if (!list || list == noexcept_false_spec)
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept34.C gcc/testsuite/g++.dg/cpp0x/noexcept34.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept34.C
@@ -0,0 +1,147 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ void f7() noexcept(1);
+ void f8() noexcept(0);
+ void f9() noexcept(b);
+ void f10() noexcept(!b);
+
+ int i;
+ static constexpr auto b = true;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S s;
+SA(noexcept(s.f1()));
+SA(!noexcept(s.f2()));
+SA(noexcept(s.f3()));
+SA(noexcept(s.f4()));
+SA(!noexcept(s.f5()));
+SA(noexcept(s.f6()));
+SA(noexcept(s.f7()));
+SA(!noexcept(s.f8()));
+SA(noexcept(s.f9()));
+SA(!noexcept(s.f10()));
+
+struct S2 {
+ struct V {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void f3() noexcept(noexcept(fn())) { }
+ void f4() noexcept(noexcept(fnx())) { }
+ void fn();
+ void fnx() noexcept;
+ } v;
+ void fn();
+ void fnx();
+};
+
+S2 s2;
+SA(!noexcept(s2.v.f1()));
+SA(noexcept(s2.v.f2()));
+SA(!noexcept(s2.v.f3()));
+SA(noexcept(s2.v.f4()));
+
+struct S3 {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S3::f1() noexcept(noexcept(fn()))
+{
+}
+
+void
+S3::f2() noexcept(noexcept(fnx()))
+{
+}
+
+struct S4 {
+ int f1 (int p) noexcept(noexcept(p)) { return p; }
+ int f2 (int p) noexcept(noexcept(p));
+ int f3 (int p = 10) noexcept(noexcept(p));
+ int f4 () noexcept(noexcept(S4{}));
+};
+
+S4 s4;
+SA(noexcept(s4.f1(1)));
+SA(noexcept(s4.f2(1)));
+SA(noexcept(s4.f3()));
+SA(noexcept(s4.f4()));
+
+template<typename T>
+struct S5 {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ int i;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S5<int> s5;
+SA(noexcept(s5.f1()));
+SA(!noexcept(s5.f2()));
+SA(noexcept(s5.f3()));
+SA(noexcept(s5.f4()));
+SA(!noexcept(s5.f5()));
+SA(noexcept(s5.f6()));
+
+template<typename T>
+struct S6 {
+ void f1() noexcept(noexcept(x));
+ T x;
+};
+
+struct S7 {
+ template<typename U>
+ void f1 () noexcept(noexcept(U(1))) { }
+
+ template<int N>
+ void f2() noexcept(noexcept(N));
+
+ template <typename _Up>
+ void f3(_Up __p) noexcept(noexcept(__p));
+};
+
+void glob();
+void globx() noexcept;
+struct S8 {
+ void f1 () noexcept(noexcept(glob()));
+ void f2 () noexcept(noexcept(globx()));
+};
+
+S8 s8;
+SA(!noexcept(s8.f1()));
+SA(noexcept(s8.f2()));
+
+struct W {
+ constexpr operator bool();
+};
+
+template<typename T>
+struct S9 {
+ S9() noexcept(noexcept(w)) { }
+ S9 &operator=(S9 &&) noexcept(T::X);
+ W w;
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept35.C gcc/testsuite/g++.dg/cpp0x/noexcept35.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept35.C
@@ -0,0 +1,26 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct S {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S::f1() noexcept // { dg-error "different exception specifier" }
+{
+}
+
+void
+S::f2() // { dg-error "different exception specifier" }
+{
+}
+
+struct S2 {
+ void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
+ void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
+ void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
+ void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept36.C
@@ -0,0 +1,9 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+template <typename _Alloc> class A {
+ typedef _Alloc _Alloc_traits;
+ A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ void m_fn2(A<char>) {}
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept37.C gcc/testsuite/g++.dg/cpp0x/noexcept37.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept37.C
@@ -0,0 +1,14 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void fn1(void());
+template <typename> class A {
+ void _M_local_data();
+ A() noexcept(_M_local_data);
+};
+
+class B {
+ void _S_initialize();
+ static void _S_initialize_once();
+};
+void B::_S_initialize() { fn1(_S_initialize_once); }
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C
new file mode 100644
index 00000000000..9e5545bc022
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept38.C
@@ -0,0 +1,23 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+ virtual void f();
+ virtual void g() noexcept;
+ virtual void h() noexcept(false);
+};
+
+struct B : A
+{
+ void f() noexcept(true);
+ void g() noexcept(true);
+ void h() noexcept(true);
+};
+
+struct D : A
+{
+ void f() noexcept(false);
+ void g() noexcept(false); // { dg-error "looser throw specifier" }
+ void h() noexcept(false);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C
new file mode 100644
index 00000000000..da7490d651c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept39.C
@@ -0,0 +1,28 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void f() noexcept(false);
+void g() noexcept(true);
+void h() noexcept;
+
+struct B {
+ friend void f() noexcept(false);
+ friend void g() noexcept(false); // { dg-error "different exception specifier" }
+ friend void h() noexcept(false); // { dg-error "different exception specifier" }
+};
+
+struct C {
+ friend void f() noexcept(true); // { dg-error "different exception specifier" }
+ friend void g() noexcept(true); // { dg-error "different exception specifier" }
+ friend void h() noexcept(true); // { dg-error "different exception specifier" }
+};
+
+void o() noexcept(false);
+void p() noexcept(true);
+void q() noexcept;
+
+struct D {
+ friend void o() noexcept(true); // { dg-error "different exception specifier" }
+ friend void p() noexcept(true);
+ friend void q() noexcept(true);
+};
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2018-12-19 20:27 C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869) Marek Polacek
@ 2019-01-04 14:45 ` Marek Polacek
2019-01-07 15:44 ` Jason Merrill
1 sibling, 0 replies; 14+ messages in thread
From: Marek Polacek @ 2019-01-04 14:45 UTC (permalink / raw)
To: GCC Patches, Jason Merrill
Ping.
On Wed, Dec 19, 2018 at 03:27:31PM -0500, Marek Polacek wrote:
> Prompted by Jon's observation in 52869, I noticed that we don't treat
> a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
> As with member function bodies, default arguments, and NSDMIs, names used in
> a noexcept-specifier of a member-function can be declared later in the class
> body, so we need to wait and parse them at the end of the class.
> For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
>
> This wasn't as easy as I'd anticipated, because I needed to make sure to
> * handle well accessing function parameters in the noexcept-specifier,
> hence the maybe_{begin,end}_member_function_processing business,
> * not regress diagnostic. See e.g. noexcept38.C for detecting "looser
> throw specifier", or noexcept39.C, friend decls and redeclaration.
> This is handled by functions like noexcept_override_late_checks and
> check_redeclaration_exception_specification. I hope that's it.
>
> Compiling libstdc++ was a fairly good stress test, and I've added a bunch
> of reduced testcases I've collected along the way.
>
> I also noticed we're not properly detecting using 'this' in static member
> functions; tracked in 88548.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2018-12-19 Marek Polacek <polacek@redhat.com>
>
> PR c++/86476 - noexcept-specifier is a complete-class context.
> PR c++/52869
> * cp-tree.def (DEFAULT_ARG): Update commentary.
> * cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro.
> (check_redeclaration_exception_specification): Declare.
> (maybe_check_throw_specifier): Declare.
> * decl.c (check_redeclaration_exception_specification): No longer
> static. Handle UNPARSED_NOEXCEPT_SPEC_P.
> * except.c (nothrow_spec_p): Accept DEFAULT_ARG in assert.
> * parser.c (cp_parser_noexcept_specification_opt,
> cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
> Forward-declare.
> (unparsed_noexcepts): New macro.
> (push_unparsed_function_queues): Update initializer.
> (cp_parser_init_declarator): Maybe save the noexcept-specifier to
> post process.
> (maybe_begin_member_function_processing): New.
> (maybe_end_member_function_processing): New.
> (cp_parser_class_specifier_1): Implement delayed parsing of
> noexcept-specifiers.
> (cp_parser_member_declaration): Maybe save the noexcept-specifier to
> post process.
> (cp_parser_save_noexcept): New.
> (cp_parser_late_noexcept_specifier): New.
> (noexcept_override_late_checks): New.
> (cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept
> instead of the normal processing if needed.
> (cp_parser_save_member_function_body): Maybe save the
> noexcept-specifier to post process.
> * parser.h (cp_unparsed_functions_entry): Add new field to carry
> a noexcept-specifier.
> * pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
> * search.c (maybe_check_throw_specifier): New function, broken out
> of...
> (check_final_overrider): ...here. Call maybe_check_throw_specifier.
> * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
> (cp_tree_equal): Handle DEFAULT_ARG.
> * typeck2.c (merge_exception_specifiers): If an unparsed noexcept
> expression has been passed, return it instead of merging it.
>
> * g++.dg/cpp0x/noexcept34.C: New test.
> * g++.dg/cpp0x/noexcept35.C: New test.
> * g++.dg/cpp0x/noexcept36.C: New test.
> * g++.dg/cpp0x/noexcept37.C: New test.
> * g++.dg/cpp0x/noexcept38.C: New test.
> * g++.dg/cpp0x/noexcept39.C: New test.
>
> diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> index 43d90eb1efb..aa8b752d8f4 100644
> --- gcc/cp/cp-tree.def
> +++ gcc/cp/cp-tree.def
> @@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
>
> /* An un-parsed default argument. Holds a vector of input tokens and
> a vector of places where the argument was instantiated before
> - parsing had occurred. */
> + parsing had occurred. This is also used for delayed NSDMIs and
> + noexcept-specifier parsing. For a noexcept-specifier, the vector
> + holds a function declaration used for late checking. */
> DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
>
> /* An uninstantiated/unevaluated noexcept-specification. For the
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index 1d806b782bd..bd3cd200fcb 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -1193,6 +1193,9 @@ struct GTY (()) tree_default_arg {
> #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
> (DEFERRED_NOEXCEPT_SPEC_P (NODE) \
> && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
> +#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
> + ((NODE) && (TREE_PURPOSE (NODE)) \
> + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
>
> struct GTY (()) tree_deferred_noexcept {
> struct tree_base base;
> @@ -6430,6 +6433,8 @@ extern bool check_array_designated_initializer (constructor_elt *,
> unsigned HOST_WIDE_INT);
> extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
> extern tree build_explicit_specifier (tree, tsubst_flags_t);
> +extern void check_redeclaration_exception_specification
> + (tree, tree);
>
> /* in decl2.c */
> extern void record_mangling (tree, bool);
> @@ -6894,6 +6899,7 @@ extern tree copied_binfo (tree, tree);
> extern tree original_binfo (tree, tree);
> extern int shared_member_p (tree);
> extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
> +extern bool maybe_check_throw_specifier (tree, tree);
>
> /* The representation of a deferred access check. */
>
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index d6028e3608c..74444db8623 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -1140,7 +1140,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
> function templates. If their exception specifications do not
> match, issue a diagnostic. */
>
> -static void
> +void
> check_redeclaration_exception_specification (tree new_decl,
> tree old_decl)
> {
> @@ -1152,6 +1152,16 @@ check_redeclaration_exception_specification (tree new_decl,
> && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
> return;
>
> + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> + and check this again after we've parsed the noexcept-specifiers
> + for real. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> + {
> + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
> + copy_decl (old_decl));
> + return;
> + }
> +
> if (!type_dependent_expression_p (old_decl))
> {
> maybe_instantiate_noexcept (new_decl);
> diff --git gcc/cp/except.c gcc/cp/except.c
> index b04eb0c5332..92fc39d3968 100644
> --- gcc/cp/except.c
> +++ gcc/cp/except.c
> @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> || TREE_VALUE (spec)
> || spec == noexcept_false_spec
> || TREE_PURPOSE (spec) == error_mark_node
> + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
> || processing_template_decl);
>
> return false;
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index b57e35d04c5..267b3518156 100644
> --- gcc/cp/parser.c
> +++ gcc/cp/parser.c
> @@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
>
> static cp_token_cache *cp_token_cache_new
> (cp_token *, cp_token *);
> +static tree cp_parser_noexcept_specification_opt
> + (cp_parser *, bool, bool *, bool);
> +static tree cp_parser_late_noexcept_specifier
> + (cp_parser *, tree);
> +static void noexcept_override_late_checks
> + (tree, tree);
>
> static void cp_parser_initial_pragma
> (cp_token *);
> @@ -1968,11 +1974,14 @@ cp_parser_context_new (cp_parser_context* next)
> parser->unparsed_queues->last ().nsdmis
> #define unparsed_classes \
> parser->unparsed_queues->last ().classes
> +#define unparsed_noexcepts \
> + parser->unparsed_queues->last ().noexcepts
>
> static void
> push_unparsed_function_queues (cp_parser *parser)
> {
> - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
> + cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
> + NULL };
> vec_safe_push (parser->unparsed_queues, e);
> }
>
> @@ -20315,7 +20324,13 @@ cp_parser_init_declarator (cp_parser* parser,
> /*asmspec=*/NULL_TREE,
> attr_chainon (attributes, prefix_attributes));
> if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> - cp_parser_save_default_args (parser, decl);
> + {
> + cp_parser_save_default_args (parser, decl);
> + /* Remember if there is a noexcept-specifier to post process. */
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, decl);
> + }
> cp_finalize_omp_declare_simd (parser, decl);
> cp_finalize_oacc_routine (parser, decl, false);
> }
> @@ -23058,6 +23073,45 @@ cp_parser_class_name (cp_parser *parser,
> return decl;
> }
>
> +/* Make sure that any member-function parameters are in scope.
> + For instance, a function's noexcept-specifier can use the function's
> + parameters:
> +
> + struct S {
> + void fn (int p) noexcept(noexcept(p));
> + };
> +
> + so we need to make sure name lookup can find them. This is used
> + when we delay parsing of the noexcept-specifier. */
> +
> +static void
> +maybe_begin_member_function_processing (tree decl)
> +{
> + begin_scope (sk_function_parms, decl);
> + tree args = DECL_ARGUMENTS (decl);
> + args = nreverse (args);
> +
> + tree next;
> + for (tree parm = args; parm; parm = next)
> + {
> + next = DECL_CHAIN (parm);
> + if (TREE_CODE (parm) == PARM_DECL)
> + pushdecl (parm);
> + }
> + /* Get the decls in their original chain order and record in the
> + function. This is all and only the PARM_DECLs that were
> + pushed into scope by the loop above. */
> + DECL_ARGUMENTS (decl) = get_local_decls ();
> +}
> +
> +/* Undo the effects of maybe_begin_member_function_processing. */
> +
> +static void
> +maybe_end_member_function_processing (void)
> +{
> + pop_bindings_and_leave_scope ();
> +}
> +
> /* Parse a class-specifier.
>
> class-specifier:
> @@ -23368,6 +23422,56 @@ cp_parser_class_specifier_1 (cp_parser* parser)
> vec_safe_truncate (unparsed_classes, 0);
> after_nsdmi_defaulted_late_checks (type);
>
> + /* If there are noexcept-specifiers that have not yet been processed,
> + take care of them now. */
> + class_type = NULL_TREE;
> + pushed_scope = NULL_TREE;
> + FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
> + {
> + if (class_type != DECL_CONTEXT (decl))
> + {
> + if (pushed_scope)
> + pop_scope (pushed_scope);
> + class_type = DECL_CONTEXT (decl);
> + pushed_scope = push_scope (class_type);
> + }
> +
> + /* Make sure that any template parameters are in scope. */
> + maybe_begin_member_template_processing (decl);
> +
> + /* Make sure that any member-function parameters are in scope. */
> + maybe_begin_member_function_processing (decl);
> +
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + spec = TREE_PURPOSE (spec);
> + vec<tree, va_gc> *p = DEFARG_INSTANTIATIONS (spec);
> + tree old_decl = (p ? (*p)[0] : NULL_TREE);
> +
> + /* Now we can parse the noexcept-specifier. */
> + spec = cp_parser_late_noexcept_specifier (parser, spec);
> + if (spec != error_mark_node)
> + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
> +
> + /* If we've stashed an old declaration, it means we need to
> + perform late redeclaration checking. */
> + if (old_decl)
> + check_redeclaration_exception_specification (decl, old_decl);
> +
> + /* The finish_struct call above performed various override checking,
> + but it skipped unparsed noexcept-specifier operands. Now that we
> + have resolved them, check again. */
> + noexcept_override_late_checks (type, decl);
> +
> + /* Remove any member-function parameters from the symbol table. */
> + maybe_end_member_function_processing ();
> +
> + /* Remove any template parameters from the symbol table. */
> + maybe_end_member_template_processing ();
> + }
> + vec_safe_truncate (unparsed_noexcepts, 0);
> + if (pushed_scope)
> + pop_scope (pushed_scope);
> +
> /* Now parse the body of the functions. */
> if (flag_openmp)
> {
> @@ -24537,6 +24641,12 @@ cp_parser_member_declaration (cp_parser* parser)
> else
> decl = finish_fully_implicit_template (parser, decl);
> }
> + if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> + {
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, decl);
> + }
> }
>
> cp_finalize_omp_declare_simd (parser, decl);
> @@ -24919,6 +25029,89 @@ cp_parser_base_specifier (cp_parser* parser)
>
> /* Exception handling [gram.exception] */
>
> +/* Save the tokens that make up the noexcept-specifier for a member-function.
> + Returns a DEFAULT_ARG. */
> +
> +static tree
> +cp_parser_save_noexcept (cp_parser *parser)
> +{
> + cp_token *first = parser->lexer->next_token;
> + /* We want everything up to, including, the final ')'. */
> + cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
> + cp_token *last = parser->lexer->next_token;
> +
> + /* As with default arguments and NSDMIs, make us of DEFAULT_ARG
> + to carry the information we will need. */
> + tree expr = make_node (DEFAULT_ARG);
> + /* Save away the noexcept-specifier; we will process it when the
> + class is complete. */
> + DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
> + DEFARG_INSTANTIATIONS (expr) = NULL;
> + expr = build_tree_list (expr, NULL_TREE);
> + return expr;
> +}
> +
> +/* Used for late processing of noexcept-specifiers of member-functions.
> + DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
> + we saved for later; parse it now. */
> +
> +static tree
> +cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
> +{
> + /* Make sure we've gotten something that hasn't been parsed yet. */
> + gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
> +
> + push_unparsed_function_queues (parser);
> +
> + /* Push the saved tokens for the noexcept-specifier onto the parser's
> + lexer stack. */
> + cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
> + cp_parser_push_lexer_for_tokens (parser, tokens);
> +
> + /* Parse the cached noexcept-specifier. */
> + tree parsed_arg
> + = cp_parser_noexcept_specification_opt (parser,
> + /*require_constexpr=*/true,
> + NULL,
> + /*return_cond=*/false);
> +
> + /* Revert to the main lexer. */
> + cp_parser_pop_lexer (parser);
> +
> + /* Restore the queue. */
> + pop_unparsed_function_queues (parser);
> +
> + /* And we're done. */
> + return parsed_arg;
> +}
> +
> +/* Perform late checking of overriding function with respect to their
> + noexcept-specifiers. TYPE is the class and FNDECL is the function
> + that potentially overrides some virtual function with the same
> + signature. */
> +
> +static void
> +noexcept_override_late_checks (tree type, tree fndecl)
> +{
> + tree binfo = TYPE_BINFO (type);
> + tree base_binfo;
> +
> + if (DECL_STATIC_FUNCTION_P (fndecl))
> + return;
> +
> + for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
> + {
> + tree basetype = BINFO_TYPE (base_binfo);
> +
> + if (!TYPE_POLYMORPHIC_P (basetype))
> + continue;
> +
> + tree fn = look_for_overrides_here (basetype, fndecl);
> + if (fn)
> + maybe_check_throw_specifier (fndecl, fn);
> + }
> +}
> +
> /* Parse an (optional) noexcept-specification.
>
> noexcept-specification:
> @@ -24947,6 +25140,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> if (cp_parser_is_keyword (token, RID_NOEXCEPT))
> {
> tree expr;
> +
> + /* [class.mem]/6 says that a noexcept-specifer (within the
> + member-specification of the class) is a complete-class context of
> + a class. So, if the noexcept-specifier has the optional expression,
> + just save the tokens, and reparse this after we're done with the
> + class. */
> + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
> + && at_class_scope_p ()
> + && TYPE_BEING_DEFINED (current_class_type)
> + && !LAMBDA_TYPE_P (current_class_type))
> + return cp_parser_save_noexcept (parser);
> +
> cp_lexer_consume_token (parser->lexer);
>
> if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
> @@ -28120,7 +28325,12 @@ cp_parser_save_member_function_body (cp_parser* parser,
> return error_mark_node;
> }
>
> - /* Remember it, if there default args to post process. */
> + /* Remember if there is a noexcept-specifier to post process. */
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, fn);
> +
> + /* Remember it, if there are default args to post process. */
> cp_parser_save_default_args (parser, fn);
>
> /* Save away the tokens that make up the body of the
> diff --git gcc/cp/parser.h gcc/cp/parser.h
> index 8bfa3f3b9c4..df0c6c1960e 100644
> --- gcc/cp/parser.h
> +++ gcc/cp/parser.h
> @@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
> /* Nested classes go in this vector, so that we can do some final
> processing after parsing any NSDMIs. */
> vec<tree, va_gc> *classes;
> +
> + /* Functions with noexcept-specifiers that require post-processing. */
> + vec<tree, va_gc> *noexcepts;
> };
>
>
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index e99de71ea9e..382a14f7d6a 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -24988,8 +24988,9 @@ dependent_type_p_r (tree type)
> if (tree noex = TREE_PURPOSE (spec))
> /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
> affect overload resolution and treating it as dependent breaks
> - things. */
> + things. Same for an unparsed noexcept expression. */
> if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
> + && TREE_CODE (noex) != DEFAULT_ARG
> && value_dependent_expression_p (noex))
> return true;
> return false;
> diff --git gcc/cp/search.c gcc/cp/search.c
> index d700fe328f4..3e6494dd98f 100644
> --- gcc/cp/search.c
> +++ gcc/cp/search.c
> @@ -1850,6 +1850,38 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
> NULL, &lfd);
> }
>
> +/* Check throw specifier of OVERRIDER is at least as strict as
> + the one of BASEFN. */
> +
> +bool
> +maybe_check_throw_specifier (tree overrider, tree basefn)
> +{
> + maybe_instantiate_noexcept (basefn);
> + maybe_instantiate_noexcept (overrider);
> + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> +
> + if (DECL_INVALID_OVERRIDER_P (overrider))
> + return true;
> +
> + /* Can't check this yet. Pretend this is fine and let
> + noexcept_override_late_checks check this later. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> + return true;
> +
> + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> + {
> + auto_diagnostic_group d;
> + error ("looser throw specifier for %q+#F", overrider);
> + inform (DECL_SOURCE_LOCATION (basefn),
> + "overridden function is %q#F", basefn);
> + DECL_INVALID_OVERRIDER_P (overrider) = 1;
> + return false;
> + }
> + return true;
> +}
> +
> /* Check that virtual overrider OVERRIDER is acceptable for base function
> BASEFN. Issue diagnostic, and return zero, if unacceptable. */
>
> @@ -1860,7 +1892,6 @@ check_final_overrider (tree overrider, tree basefn)
> tree base_type = TREE_TYPE (basefn);
> tree over_return = fndecl_declared_return_type (overrider);
> tree base_return = fndecl_declared_return_type (basefn);
> - tree over_throw, base_throw;
>
> int fail = 0;
>
> @@ -1944,21 +1975,8 @@ check_final_overrider (tree overrider, tree basefn)
> return 0;
> }
>
> - /* Check throw specifier is at least as strict. */
> - maybe_instantiate_noexcept (basefn);
> - maybe_instantiate_noexcept (overrider);
> - base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> - over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> -
> - if (!comp_except_specs (base_throw, over_throw, ce_derived))
> - {
> - auto_diagnostic_group d;
> - error ("looser throw specifier for %q+#F", overrider);
> - inform (DECL_SOURCE_LOCATION (basefn),
> - "overridden function is %q#F", basefn);
> - DECL_INVALID_OVERRIDER_P (overrider) = 1;
> - return 0;
> - }
> + if (!maybe_check_throw_specifier (overrider, basefn))
> + return 0;
>
> /* Check for conflicting type attributes. But leave transaction_safe for
> set_one_vmethod_tm_attributes. */
> diff --git gcc/cp/tree.c gcc/cp/tree.c
> index 97074dfab56..a7d1e58c73a 100644
> --- gcc/cp/tree.c
> +++ gcc/cp/tree.c
> @@ -2542,6 +2542,7 @@ canonical_eh_spec (tree raises)
> if (raises == NULL_TREE)
> return raises;
> else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
> + || UNPARSED_NOEXCEPT_SPEC_P (raises)
> || uses_template_parms (raises)
> || uses_template_parms (TREE_PURPOSE (raises)))
> /* Keep a dependent or deferred exception specification. */
> @@ -3653,6 +3654,7 @@ cp_tree_equal (tree t1, tree t2)
> case TEMPLATE_DECL:
> case IDENTIFIER_NODE:
> case SSA_NAME:
> + case DEFAULT_ARG:
> return false;
>
> case BASELINK:
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index 64e36efd17e..6fec77d8269 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2302,6 +2302,12 @@ merge_exception_specifiers (tree list, tree add)
> {
> tree noex, orig_list;
>
> + /* We don't want to lose the unparsed operand lest we miss diagnostics. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> + return list;
> + else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> + return add;
> +
> /* No exception-specifier or noexcept(false) are less strict than
> anything else. Prefer the newer variant (LIST). */
> if (!list || list == noexcept_false_spec)
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept34.C gcc/testsuite/g++.dg/cpp0x/noexcept34.C
> new file mode 100644
> index 00000000000..43b38c2446f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept34.C
> @@ -0,0 +1,147 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert(X, #X)
> +
> +struct S {
> + void f1() noexcept(noexcept(i)) { }
> + void f2() noexcept(noexcept(fn())) { }
> + void f3() noexcept(noexcept(fnx())) { }
> + void f4() noexcept(noexcept(i));
> + void f5() noexcept(noexcept(fn()));
> + void f6() noexcept(noexcept(fnx()));
> +
> + void f7() noexcept(1);
> + void f8() noexcept(0);
> + void f9() noexcept(b);
> + void f10() noexcept(!b);
> +
> + int i;
> + static constexpr auto b = true;
> + void fny() noexcept(noexcept(fn()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +S s;
> +SA(noexcept(s.f1()));
> +SA(!noexcept(s.f2()));
> +SA(noexcept(s.f3()));
> +SA(noexcept(s.f4()));
> +SA(!noexcept(s.f5()));
> +SA(noexcept(s.f6()));
> +SA(noexcept(s.f7()));
> +SA(!noexcept(s.f8()));
> +SA(noexcept(s.f9()));
> +SA(!noexcept(s.f10()));
> +
> +struct S2 {
> + struct V {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void f3() noexcept(noexcept(fn())) { }
> + void f4() noexcept(noexcept(fnx())) { }
> + void fn();
> + void fnx() noexcept;
> + } v;
> + void fn();
> + void fnx();
> +};
> +
> +S2 s2;
> +SA(!noexcept(s2.v.f1()));
> +SA(noexcept(s2.v.f2()));
> +SA(!noexcept(s2.v.f3()));
> +SA(noexcept(s2.v.f4()));
> +
> +struct S3 {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +void
> +S3::f1() noexcept(noexcept(fn()))
> +{
> +}
> +
> +void
> +S3::f2() noexcept(noexcept(fnx()))
> +{
> +}
> +
> +struct S4 {
> + int f1 (int p) noexcept(noexcept(p)) { return p; }
> + int f2 (int p) noexcept(noexcept(p));
> + int f3 (int p = 10) noexcept(noexcept(p));
> + int f4 () noexcept(noexcept(S4{}));
> +};
> +
> +S4 s4;
> +SA(noexcept(s4.f1(1)));
> +SA(noexcept(s4.f2(1)));
> +SA(noexcept(s4.f3()));
> +SA(noexcept(s4.f4()));
> +
> +template<typename T>
> +struct S5 {
> + void f1() noexcept(noexcept(i)) { }
> + void f2() noexcept(noexcept(fn())) { }
> + void f3() noexcept(noexcept(fnx())) { }
> + void f4() noexcept(noexcept(i));
> + void f5() noexcept(noexcept(fn()));
> + void f6() noexcept(noexcept(fnx()));
> +
> + int i;
> + void fny() noexcept(noexcept(fn()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +S5<int> s5;
> +SA(noexcept(s5.f1()));
> +SA(!noexcept(s5.f2()));
> +SA(noexcept(s5.f3()));
> +SA(noexcept(s5.f4()));
> +SA(!noexcept(s5.f5()));
> +SA(noexcept(s5.f6()));
> +
> +template<typename T>
> +struct S6 {
> + void f1() noexcept(noexcept(x));
> + T x;
> +};
> +
> +struct S7 {
> + template<typename U>
> + void f1 () noexcept(noexcept(U(1))) { }
> +
> + template<int N>
> + void f2() noexcept(noexcept(N));
> +
> + template <typename _Up>
> + void f3(_Up __p) noexcept(noexcept(__p));
> +};
> +
> +void glob();
> +void globx() noexcept;
> +struct S8 {
> + void f1 () noexcept(noexcept(glob()));
> + void f2 () noexcept(noexcept(globx()));
> +};
> +
> +S8 s8;
> +SA(!noexcept(s8.f1()));
> +SA(noexcept(s8.f2()));
> +
> +struct W {
> + constexpr operator bool();
> +};
> +
> +template<typename T>
> +struct S9 {
> + S9() noexcept(noexcept(w)) { }
> + S9 &operator=(S9 &&) noexcept(T::X);
> + W w;
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept35.C gcc/testsuite/g++.dg/cpp0x/noexcept35.C
> new file mode 100644
> index 00000000000..b3859de9ebc
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept35.C
> @@ -0,0 +1,26 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +struct S {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +void
> +S::f1() noexcept // { dg-error "different exception specifier" }
> +{
> +}
> +
> +void
> +S::f2() // { dg-error "different exception specifier" }
> +{
> +}
> +
> +struct S2 {
> + void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
> + void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
> + void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
> + void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept36.C gcc/testsuite/g++.dg/cpp0x/noexcept36.C
> new file mode 100644
> index 00000000000..12c6d364099
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept36.C
> @@ -0,0 +1,9 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +template <typename _Alloc> class A {
> + typedef _Alloc _Alloc_traits;
> + A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> + A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> + void m_fn2(A<char>) {}
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept37.C gcc/testsuite/g++.dg/cpp0x/noexcept37.C
> new file mode 100644
> index 00000000000..a81032f28e9
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept37.C
> @@ -0,0 +1,14 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +void fn1(void());
> +template <typename> class A {
> + void _M_local_data();
> + A() noexcept(_M_local_data);
> +};
> +
> +class B {
> + void _S_initialize();
> + static void _S_initialize_once();
> +};
> +void B::_S_initialize() { fn1(_S_initialize_once); }
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept38.C gcc/testsuite/g++.dg/cpp0x/noexcept38.C
> new file mode 100644
> index 00000000000..9e5545bc022
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept38.C
> @@ -0,0 +1,23 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +struct A
> +{
> + virtual void f();
> + virtual void g() noexcept;
> + virtual void h() noexcept(false);
> +};
> +
> +struct B : A
> +{
> + void f() noexcept(true);
> + void g() noexcept(true);
> + void h() noexcept(true);
> +};
> +
> +struct D : A
> +{
> + void f() noexcept(false);
> + void g() noexcept(false); // { dg-error "looser throw specifier" }
> + void h() noexcept(false);
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept39.C gcc/testsuite/g++.dg/cpp0x/noexcept39.C
> new file mode 100644
> index 00000000000..da7490d651c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept39.C
> @@ -0,0 +1,28 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +void f() noexcept(false);
> +void g() noexcept(true);
> +void h() noexcept;
> +
> +struct B {
> + friend void f() noexcept(false);
> + friend void g() noexcept(false); // { dg-error "different exception specifier" }
> + friend void h() noexcept(false); // { dg-error "different exception specifier" }
> +};
> +
> +struct C {
> + friend void f() noexcept(true); // { dg-error "different exception specifier" }
> + friend void g() noexcept(true); // { dg-error "different exception specifier" }
> + friend void h() noexcept(true); // { dg-error "different exception specifier" }
> +};
> +
> +void o() noexcept(false);
> +void p() noexcept(true);
> +void q() noexcept;
> +
> +struct D {
> + friend void o() noexcept(true); // { dg-error "different exception specifier" }
> + friend void p() noexcept(true);
> + friend void q() noexcept(true);
> +};
Marek
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2018-12-19 20:27 C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869) Marek Polacek
2019-01-04 14:45 ` Marek Polacek
@ 2019-01-07 15:44 ` Jason Merrill
2019-05-10 19:21 ` Marek Polacek
1 sibling, 1 reply; 14+ messages in thread
From: Jason Merrill @ 2019-01-07 15:44 UTC (permalink / raw)
To: Marek Polacek, GCC Patches
On 12/19/18 3:27 PM, Marek Polacek wrote:
> Prompted by Jon's observation in 52869, I noticed that we don't treat
> a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
> As with member function bodies, default arguments, and NSDMIs, names used in
> a noexcept-specifier of a member-function can be declared later in the class
> body, so we need to wait and parse them at the end of the class.
> For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
Or DEFERRED_PARSE, yes.
> + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> + and check this again after we've parsed the noexcept-specifiers
> + for real. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> + {
> + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
> + copy_decl (old_decl));
> + return;
> + }
Why copy_decl?
It seems wasteful to allocate a vec to hold this single decl; let's make
the last field of tree_default_arg a union instead. And add a new macro
for the single decl case.
I notice that default_arg currently uses tree_common for some reason,
and we ought to be able to save two words by switching to tree_base
> @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> || TREE_VALUE (spec)
> || spec == noexcept_false_spec
> || TREE_PURPOSE (spec) == error_mark_node
> + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
Maybe use UNPARSED_NOEXCEPT_SPEC_P here?
> +/* Make sure that any member-function parameters are in scope.
> + For instance, a function's noexcept-specifier can use the function's
> + parameters:
> +
> + struct S {
> + void fn (int p) noexcept(noexcept(p));
> + };
> +
> + so we need to make sure name lookup can find them. This is used
> + when we delay parsing of the noexcept-specifier. */
> +
> +static void
> +maybe_begin_member_function_processing (tree decl)
This name is pretty misleading. How about inject_parm_decls, to go with
inject_this_parameter?
> +/* Undo the effects of maybe_begin_member_function_processing. */
> +
> +static void
> +maybe_end_member_function_processing (void)
And then perhaps pop_injected_parms.
> +/* Check throw specifier of OVERRIDER is at least as strict as
> + the one of BASEFN. */
> +
> +bool
> +maybe_check_throw_specifier (tree overrider, tree basefn)
> +{
> + maybe_instantiate_noexcept (basefn);
> + maybe_instantiate_noexcept (overrider);
> + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> +
> + if (DECL_INVALID_OVERRIDER_P (overrider))
> + return true;
> +
> + /* Can't check this yet. Pretend this is fine and let
> + noexcept_override_late_checks check this later. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> + return true;
> +
> + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> + {
> + auto_diagnostic_group d;
> + error ("looser throw specifier for %q+#F", overrider);
Since we're touching this diagnostic, let's correct it now to "exception
specification". And add "on overriding virtual function".
Jason
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-01-07 15:44 ` Jason Merrill
@ 2019-05-10 19:21 ` Marek Polacek
2019-05-17 14:35 ` Marek Polacek
2019-05-28 15:48 ` Jason Merrill
0 siblings, 2 replies; 14+ messages in thread
From: Marek Polacek @ 2019-05-10 19:21 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
Coming back to this. I didn't think this was suitable for GCC 9.
On Mon, Jan 07, 2019 at 10:44:37AM -0500, Jason Merrill wrote:
> On 12/19/18 3:27 PM, Marek Polacek wrote:
> > Prompted by Jon's observation in 52869, I noticed that we don't treat
> > a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
> > As with member function bodies, default arguments, and NSDMIs, names used in
> > a noexcept-specifier of a member-function can be declared later in the class
> > body, so we need to wait and parse them at the end of the class.
> > For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
>
> Or DEFERRED_PARSE, yes.
I didn't change the name but I'm happy to do it as a follow up.
> > + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> > + and check this again after we've parsed the noexcept-specifiers
> > + for real. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > + {
> > + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
> > + copy_decl (old_decl));
> > + return;
> > + }
>
> Why copy_decl?
This is so that we don't lose the diagnostic in noexcept46.C. If I don't use
copy_decl then the tree is shared and subsequent changes to it make us not
detect discrepancies like noexcept(false) vs. noexcept(true) on the same decl.
> It seems wasteful to allocate a vec to hold this single decl; let's make the
> last field of tree_default_arg a union instead. And add a new macro for the
> single decl case.
Done. But that required also adding GTY markers *and* a new BOOL_BITFIELD for
the sake of GTY((desc)).
> I notice that default_arg currently uses tree_common for some reason, and we
> ought to be able to save two words by switching to tree_base
Done.
> > @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> > || TREE_VALUE (spec)
> > || spec == noexcept_false_spec
> > || TREE_PURPOSE (spec) == error_mark_node
> > + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
>
> Maybe use UNPARSED_NOEXCEPT_SPEC_P here?
Done.
> > +/* Make sure that any member-function parameters are in scope.
> > + For instance, a function's noexcept-specifier can use the function's
> > + parameters:
> > +
> > + struct S {
> > + void fn (int p) noexcept(noexcept(p));
> > + };
> > +
> > + so we need to make sure name lookup can find them. This is used
> > + when we delay parsing of the noexcept-specifier. */
> > +
> > +static void
> > +maybe_begin_member_function_processing (tree decl)
>
> This name is pretty misleading. How about inject_parm_decls, to go with
> inject_this_parameter?
Done.
> > +/* Undo the effects of maybe_begin_member_function_processing. */
> > +
> > +static void
> > +maybe_end_member_function_processing (void)
>
> And then perhaps pop_injected_parms.
Done.
> > +/* Check throw specifier of OVERRIDER is at least as strict as
> > + the one of BASEFN. */
> > +
> > +bool
> > +maybe_check_throw_specifier (tree overrider, tree basefn)
> > +{
> > + maybe_instantiate_noexcept (basefn);
> > + maybe_instantiate_noexcept (overrider);
> > + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> > + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> > +
> > + if (DECL_INVALID_OVERRIDER_P (overrider))
> > + return true;
> > +
> > + /* Can't check this yet. Pretend this is fine and let
> > + noexcept_override_late_checks check this later. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> > + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> > + return true;
> > +
> > + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> > + {
> > + auto_diagnostic_group d;
> > + error ("looser throw specifier for %q+#F", overrider);
>
> Since we're touching this diagnostic, let's correct it now to "exception
> specification". And add "on overriding virtual function".
Ok, changed to the more up-to-date term.
Two further changes were required since my changes to detecting 'this' for
static member functions:
1) use THIS_FORBIDDEN if needed when parsing the delayed noexcept,
2) careful about friend member functions -- its DECL_CONTEXT is not the
containing class, need to use DECL_FRIEND_CONTEXT.
Both of these points are tested in g++.dg/cpp0x/this1.C.
Bootstrapped/regtested on x86_64-linux, ok for trunk?
2019-05-10 Marek Polacek <polacek@redhat.com>
PR c++/86476 - noexcept-specifier is a complete-class context.
PR c++/52869
* cp-tree.def (DEFAULT_ARG): Update commentary.
* cp-tree.h (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P):
New macros.
(tree_default_arg): Add a tree field, make the last two fields into a
union. Add GTY markers.
(check_redeclaration_exception_specification): Declare.
(maybe_check_throw_specifier): Declare.
* decl.c (check_redeclaration_exception_specification): No longer
static. Handle UNPARSED_NOEXCEPT_SPEC_P.
* except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
* parser.c (cp_parser_noexcept_specification_opt,
cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
Forward-declare.
(unparsed_noexcepts): New macro.
(push_unparsed_function_queues): Update initializer.
(cp_parser_init_declarator): Maybe save the noexcept-specifier to
post process.
(inject_parm_decls): New.
(pop_injected_parms): New.
(cp_parser_class_specifier_1): Implement delayed parsing of
noexcept-specifiers.
(cp_parser_member_declaration): Maybe save the noexcept-specifier to
post process.
(cp_parser_save_noexcept): New.
(cp_parser_late_noexcept_specifier): New.
(noexcept_override_late_checks): New.
(cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept
instead of the normal processing if needed.
(cp_parser_save_member_function_body): Maybe save the
noexcept-specifier to post process.
* parser.h (cp_unparsed_functions_entry): Add new field to carry
a noexcept-specifier.
* pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
* search.c (maybe_check_throw_specifier): New function, broken out
of...
(check_final_overrider): ...here. Call maybe_check_throw_specifier.
* tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
(cp_tree_equal): Handle DEFAULT_ARG.
* typeck2.c (merge_exception_specifiers): If an unparsed noexcept
expression has been passed, return it instead of merging it.
* g++.dg/cpp0x/noexcept41.C: New test.
* g++.dg/cpp0x/noexcept42.C: New test.
* g++.dg/cpp0x/noexcept43.C: New test.
* g++.dg/cpp0x/noexcept44.C: New test.
* g++.dg/cpp0x/noexcept45.C: New test.
* g++.dg/cpp0x/noexcept46.C: New test.
* g++.dg/eh/shadow1.C: Adjust dg-error.
diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 03c105b5c4c..33eb5d25efe 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
/* An un-parsed default argument. Holds a vector of input tokens and
a vector of places where the argument was instantiated before
- parsing had occurred. */
+ parsing had occurred. This is also used for delayed NSDMIs and
+ noexcept-specifier parsing. For a noexcept-specifier, we use a tree
+ holding a function declaration used for late checking. */
DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
/* An uninstantiated/unevaluated noexcept-specification. For the
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index f253857b02a..ef14a011293 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1178,12 +1178,20 @@ enum cp_identifier_kind {
#define DEFARG_TOKENS(NODE) \
(((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens)
#define DEFARG_INSTANTIATIONS(NODE) \
- (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
+ (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations)
+#define DEFARG_DECL(NODE) \
+ (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl)
+#define DEFARG_NOEXCEPT_P(NODE) \
+ (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p)
struct GTY (()) tree_default_arg {
- struct tree_common common;
+ struct tree_base base;
struct cp_token_cache *tokens;
- vec<tree, va_gc> *instantiations;
+ BOOL_BITFIELD noexcept_p : 1;
+ union {
+ vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
+ tree GTY((tag ("1"))) decl;
+ } GTY((desc ("%1.noexcept_p"))) u;
};
@@ -1197,6 +1205,9 @@ struct GTY (()) tree_default_arg {
#define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
(DEFERRED_NOEXCEPT_SPEC_P (NODE) \
&& DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
+#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
+ ((NODE) && (TREE_PURPOSE (NODE)) \
+ && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
struct GTY (()) tree_deferred_noexcept {
struct tree_base base;
@@ -6464,6 +6475,8 @@ extern bool check_array_designated_initializer (constructor_elt *,
unsigned HOST_WIDE_INT);
extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
extern tree build_explicit_specifier (tree, tsubst_flags_t);
+extern void check_redeclaration_exception_specification
+ (tree, tree);
/* in decl2.c */
extern void record_mangling (tree, bool);
@@ -6929,6 +6942,7 @@ extern tree copied_binfo (tree, tree);
extern tree original_binfo (tree, tree);
extern int shared_member_p (tree);
extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
+extern bool maybe_check_throw_specifier (tree, tree);
/* The representation of a deferred access check. */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 36014dc628e..a2effa13623 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
function templates. If their exception specifications do not
match, issue a diagnostic. */
-static void
+void
check_redeclaration_exception_specification (tree new_decl,
tree old_decl)
{
@@ -1151,6 +1151,15 @@ check_redeclaration_exception_specification (tree new_decl,
&& UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
return;
+ /* We can't compare unparsed noexcept-specifiers. Save the old decl
+ and check this again after we've parsed the noexcept-specifiers
+ for real. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
+ {
+ DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
+ return;
+ }
+
if (!type_dependent_expression_p (old_decl))
{
maybe_instantiate_noexcept (new_decl);
diff --git gcc/cp/except.c gcc/cp/except.c
index afc261073d7..208c9c1461d 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1248,6 +1248,7 @@ nothrow_spec_p (const_tree spec)
|| TREE_VALUE (spec)
|| spec == noexcept_false_spec
|| TREE_PURPOSE (spec) == error_mark_node
+ || UNPARSED_NOEXCEPT_SPEC_P (spec)
|| processing_template_decl);
return false;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 12beadf5096..41197ab3486 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
static cp_token_cache *cp_token_cache_new
(cp_token *, cp_token *);
+static tree cp_parser_noexcept_specification_opt
+ (cp_parser *, bool, bool *, bool);
+static tree cp_parser_late_noexcept_specifier
+ (cp_parser *, tree);
+static void noexcept_override_late_checks
+ (tree, tree);
static void cp_parser_initial_pragma
(cp_token *);
@@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_classes \
parser->unparsed_queues->last ().classes
+#define unparsed_noexcepts \
+ parser->unparsed_queues->last ().noexcepts
static void
push_unparsed_function_queues (cp_parser *parser)
{
- cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
+ cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
+ NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
/*asmspec=*/NULL_TREE,
attr_chainon (attributes, prefix_attributes));
if (decl && TREE_CODE (decl) == FUNCTION_DECL)
- cp_parser_save_default_args (parser, decl);
+ {
+ cp_parser_save_default_args (parser, decl);
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
+ }
cp_finalize_omp_declare_simd (parser, decl);
cp_finalize_oacc_routine (parser, decl, false);
}
@@ -23334,6 +23349,45 @@ cp_parser_class_name (cp_parser *parser,
return decl;
}
+/* Make sure that any member-function parameters are in scope.
+ For instance, a function's noexcept-specifier can use the function's
+ parameters:
+
+ struct S {
+ void fn (int p) noexcept(noexcept(p));
+ };
+
+ so we need to make sure name lookup can find them. This is used
+ when we delay parsing of the noexcept-specifier. */
+
+static void
+inject_parm_decls (tree decl)
+{
+ begin_scope (sk_function_parms, decl);
+ tree args = DECL_ARGUMENTS (decl);
+ args = nreverse (args);
+
+ tree next;
+ for (tree parm = args; parm; parm = next)
+ {
+ next = DECL_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ pushdecl (parm);
+ }
+ /* Get the decls in their original chain order and record in the
+ function. This is all and only the PARM_DECLs that were
+ pushed into scope by the loop above. */
+ DECL_ARGUMENTS (decl) = get_local_decls ();
+}
+
+/* Undo the effects of inject_parm_decls. */
+
+static void
+pop_injected_parms (void)
+{
+ pop_bindings_and_leave_scope ();
+}
+
/* Parse a class-specifier.
class-specifier:
@@ -23644,6 +23698,66 @@ cp_parser_class_specifier_1 (cp_parser* parser)
vec_safe_truncate (unparsed_classes, 0);
after_nsdmi_defaulted_late_checks (type);
+ /* If there are noexcept-specifiers that have not yet been processed,
+ take care of them now. */
+ class_type = NULL_TREE;
+ pushed_scope = NULL_TREE;
+ FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+ {
+ tree ctx = (DECL_FRIEND_P (decl) ? DECL_FRIEND_CONTEXT (decl)
+ : DECL_CONTEXT (decl));
+ if (class_type != ctx)
+ {
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+ class_type = ctx;
+ pushed_scope = push_scope (class_type);
+ }
+
+ /* Make sure that any template parameters are in scope. */
+ maybe_begin_member_template_processing (decl);
+
+ /* Make sure that any member-function parameters are in scope. */
+ inject_parm_decls (decl);
+
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ spec = TREE_PURPOSE (spec);
+ tree old_decl = DEFARG_DECL (spec);
+
+ /* 'this' is not allowed in static member functions. */
+ unsigned char local_variables_forbidden_p
+ = parser->local_variables_forbidden_p;
+ if (DECL_THIS_STATIC (decl))
+ parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+
+ /* Now we can parse the noexcept-specifier. */
+ spec = cp_parser_late_noexcept_specifier (parser, spec);
+
+ /* Restore the state of local_variables_forbidden_p. */
+ parser->local_variables_forbidden_p = local_variables_forbidden_p;
+ if (spec != error_mark_node)
+ TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
+
+ /* If we've stashed an old declaration, it means we need to
+ perform late redeclaration checking. */
+ if (old_decl)
+ check_redeclaration_exception_specification (decl, old_decl);
+
+ /* The finish_struct call above performed various override checking,
+ but it skipped unparsed noexcept-specifier operands. Now that we
+ have resolved them, check again. */
+ noexcept_override_late_checks (type, decl);
+
+ /* Remove any member-function parameters from the symbol table. */
+ pop_injected_parms ();
+
+ /* Remove any template parameters from the symbol table. */
+ maybe_end_member_template_processing ();
+ }
+ vec_safe_truncate (unparsed_noexcepts, 0);
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+
/* Now parse the body of the functions. */
if (flag_openmp)
{
@@ -24817,6 +24931,12 @@ cp_parser_member_declaration (cp_parser* parser)
else
decl = finish_fully_implicit_template (parser, decl);
}
+ if (decl && TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
+ }
}
cp_finalize_omp_declare_simd (parser, decl);
@@ -25199,6 +25319,90 @@ cp_parser_base_specifier (cp_parser* parser)
/* Exception handling [gram.exception] */
+/* Save the tokens that make up the noexcept-specifier for a member-function.
+ Returns a DEFAULT_ARG. */
+
+static tree
+cp_parser_save_noexcept (cp_parser *parser)
+{
+ cp_token *first = parser->lexer->next_token;
+ /* We want everything up to, including, the final ')'. */
+ cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
+ cp_token *last = parser->lexer->next_token;
+
+ /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
+ to carry the information we will need. */
+ tree expr = make_node (DEFAULT_ARG);
+ /* Save away the noexcept-specifier; we will process it when the
+ class is complete. */
+ DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
+ DEFARG_DECL (expr) = NULL_TREE;
+ DEFARG_NOEXCEPT_P (expr) = true;
+ expr = build_tree_list (expr, NULL_TREE);
+ return expr;
+}
+
+/* Used for late processing of noexcept-specifiers of member-functions.
+ DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
+ we saved for later; parse it now. */
+
+static tree
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+{
+ /* Make sure we've gotten something that hasn't been parsed yet. */
+ gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
+
+ push_unparsed_function_queues (parser);
+
+ /* Push the saved tokens for the noexcept-specifier onto the parser's
+ lexer stack. */
+ cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
+ cp_parser_push_lexer_for_tokens (parser, tokens);
+
+ /* Parse the cached noexcept-specifier. */
+ tree parsed_arg
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ NULL,
+ /*return_cond=*/false);
+
+ /* Revert to the main lexer. */
+ cp_parser_pop_lexer (parser);
+
+ /* Restore the queue. */
+ pop_unparsed_function_queues (parser);
+
+ /* And we're done. */
+ return parsed_arg;
+}
+
+/* Perform late checking of overriding function with respect to their
+ noexcept-specifiers. TYPE is the class and FNDECL is the function
+ that potentially overrides some virtual function with the same
+ signature. */
+
+static void
+noexcept_override_late_checks (tree type, tree fndecl)
+{
+ tree binfo = TYPE_BINFO (type);
+ tree base_binfo;
+
+ if (DECL_STATIC_FUNCTION_P (fndecl))
+ return;
+
+ for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+ {
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ if (!TYPE_POLYMORPHIC_P (basetype))
+ continue;
+
+ tree fn = look_for_overrides_here (basetype, fndecl);
+ if (fn)
+ maybe_check_throw_specifier (fndecl, fn);
+ }
+}
+
/* Parse an (optional) noexcept-specification.
noexcept-specification:
@@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
if (cp_parser_is_keyword (token, RID_NOEXCEPT))
{
tree expr;
+
+ /* [class.mem]/6 says that a noexcept-specifer (within the
+ member-specification of the class) is a complete-class context of
+ a class. So, if the noexcept-specifier has the optional expression,
+ just save the tokens, and reparse this after we're done with the
+ class. */
+ if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
+ && at_class_scope_p ()
+ && TYPE_BEING_DEFINED (current_class_type)
+ && !LAMBDA_TYPE_P (current_class_type))
+ return cp_parser_save_noexcept (parser);
+
cp_lexer_consume_token (parser->lexer);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
@@ -28427,7 +28643,12 @@ cp_parser_save_member_function_body (cp_parser* parser,
return error_mark_node;
}
- /* Remember it, if there default args to post process. */
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, fn);
+
+ /* Remember it, if there are default args to post process. */
cp_parser_save_default_args (parser, fn);
/* Save away the tokens that make up the body of the
diff --git gcc/cp/parser.h gcc/cp/parser.h
index c03a9d87af5..2890788f489 100644
--- gcc/cp/parser.h
+++ gcc/cp/parser.h
@@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Nested classes go in this vector, so that we can do some final
processing after parsing any NSDMIs. */
vec<tree, va_gc> *classes;
+
+ /* Functions with noexcept-specifiers that require post-processing. */
+ vec<tree, va_gc> *noexcepts;
};
diff --git gcc/cp/pt.c gcc/cp/pt.c
index d6976e08690..c00d14fd954 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -25308,8 +25308,9 @@ dependent_type_p_r (tree type)
if (tree noex = TREE_PURPOSE (spec))
/* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
affect overload resolution and treating it as dependent breaks
- things. */
+ things. Same for an unparsed noexcept expression. */
if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
+ && TREE_CODE (noex) != DEFAULT_ARG
&& value_dependent_expression_p (noex))
return true;
return false;
diff --git gcc/cp/search.c gcc/cp/search.c
index 4c3fffda717..5a3a0cf2824 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -1863,6 +1863,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
NULL, &lfd);
}
+/* Check throw specifier of OVERRIDER is at least as strict as
+ the one of BASEFN. */
+
+bool
+maybe_check_throw_specifier (tree overrider, tree basefn)
+{
+ maybe_instantiate_noexcept (basefn);
+ maybe_instantiate_noexcept (overrider);
+ tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+ tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
+ if (DECL_INVALID_OVERRIDER_P (overrider))
+ return true;
+
+ /* Can't check this yet. Pretend this is fine and let
+ noexcept_override_late_checks check this later. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
+ || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
+ return true;
+
+ if (!comp_except_specs (base_throw, over_throw, ce_derived))
+ {
+ auto_diagnostic_group d;
+ error ("looser exception specification on overriding virtual function "
+ "%q+#F", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %q#F", basefn);
+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
+ return false;
+ }
+ return true;
+}
+
/* Check that virtual overrider OVERRIDER is acceptable for base function
BASEFN. Issue diagnostic, and return zero, if unacceptable. */
@@ -1873,7 +1906,6 @@ check_final_overrider (tree overrider, tree basefn)
tree base_type = TREE_TYPE (basefn);
tree over_return = fndecl_declared_return_type (overrider);
tree base_return = fndecl_declared_return_type (basefn);
- tree over_throw, base_throw;
int fail = 0;
@@ -1957,21 +1989,8 @@ check_final_overrider (tree overrider, tree basefn)
return 0;
}
- /* Check throw specifier is at least as strict. */
- maybe_instantiate_noexcept (basefn);
- maybe_instantiate_noexcept (overrider);
- base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
- over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
-
- if (!comp_except_specs (base_throw, over_throw, ce_derived))
- {
- auto_diagnostic_group d;
- error ("looser throw specifier for %q+#F", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#F", basefn);
- DECL_INVALID_OVERRIDER_P (overrider) = 1;
- return 0;
- }
+ if (!maybe_check_throw_specifier (overrider, basefn))
+ return 0;
/* Check for conflicting type attributes. But leave transaction_safe for
set_one_vmethod_tm_attributes. */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 718eed349c6..bc0080d6720 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2550,6 +2550,7 @@ canonical_eh_spec (tree raises)
if (raises == NULL_TREE)
return raises;
else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+ || UNPARSED_NOEXCEPT_SPEC_P (raises)
|| uses_template_parms (raises)
|| uses_template_parms (TREE_PURPOSE (raises)))
/* Keep a dependent or deferred exception specification. */
@@ -3662,6 +3663,7 @@ cp_tree_equal (tree t1, tree t2)
case IDENTIFIER_NODE:
case SSA_NAME:
case USING_DECL:
+ case DEFAULT_ARG:
return false;
case BASELINK:
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index df002a1664c..8cbc48fb44f 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
if (list == error_mark_node || add == error_mark_node)
return error_mark_node;
+ /* We don't want to lose the unparsed operand lest we miss diagnostics. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (list))
+ return list;
+ else if (UNPARSED_NOEXCEPT_SPEC_P (add))
+ return add;
+
/* No exception-specifier or noexcept(false) are less strict than
anything else. Prefer the newer variant (LIST). */
if (!list || list == noexcept_false_spec)
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C gcc/testsuite/g++.dg/cpp0x/noexcept41.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept41.C
@@ -0,0 +1,147 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ void f7() noexcept(1);
+ void f8() noexcept(0);
+ void f9() noexcept(b);
+ void f10() noexcept(!b);
+
+ int i;
+ static constexpr auto b = true;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S s;
+SA(noexcept(s.f1()));
+SA(!noexcept(s.f2()));
+SA(noexcept(s.f3()));
+SA(noexcept(s.f4()));
+SA(!noexcept(s.f5()));
+SA(noexcept(s.f6()));
+SA(noexcept(s.f7()));
+SA(!noexcept(s.f8()));
+SA(noexcept(s.f9()));
+SA(!noexcept(s.f10()));
+
+struct S2 {
+ struct V {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void f3() noexcept(noexcept(fn())) { }
+ void f4() noexcept(noexcept(fnx())) { }
+ void fn();
+ void fnx() noexcept;
+ } v;
+ void fn();
+ void fnx();
+};
+
+S2 s2;
+SA(!noexcept(s2.v.f1()));
+SA(noexcept(s2.v.f2()));
+SA(!noexcept(s2.v.f3()));
+SA(noexcept(s2.v.f4()));
+
+struct S3 {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S3::f1() noexcept(noexcept(fn()))
+{
+}
+
+void
+S3::f2() noexcept(noexcept(fnx()))
+{
+}
+
+struct S4 {
+ int f1 (int p) noexcept(noexcept(p)) { return p; }
+ int f2 (int p) noexcept(noexcept(p));
+ int f3 (int p = 10) noexcept(noexcept(p));
+ int f4 () noexcept(noexcept(S4{}));
+};
+
+S4 s4;
+SA(noexcept(s4.f1(1)));
+SA(noexcept(s4.f2(1)));
+SA(noexcept(s4.f3()));
+SA(noexcept(s4.f4()));
+
+template<typename T>
+struct S5 {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ int i;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S5<int> s5;
+SA(noexcept(s5.f1()));
+SA(!noexcept(s5.f2()));
+SA(noexcept(s5.f3()));
+SA(noexcept(s5.f4()));
+SA(!noexcept(s5.f5()));
+SA(noexcept(s5.f6()));
+
+template<typename T>
+struct S6 {
+ void f1() noexcept(noexcept(x));
+ T x;
+};
+
+struct S7 {
+ template<typename U>
+ void f1 () noexcept(noexcept(U(1))) { }
+
+ template<int N>
+ void f2() noexcept(noexcept(N));
+
+ template <typename _Up>
+ void f3(_Up __p) noexcept(noexcept(__p));
+};
+
+void glob();
+void globx() noexcept;
+struct S8 {
+ void f1 () noexcept(noexcept(glob()));
+ void f2 () noexcept(noexcept(globx()));
+};
+
+S8 s8;
+SA(!noexcept(s8.f1()));
+SA(noexcept(s8.f2()));
+
+struct W {
+ constexpr operator bool();
+};
+
+template<typename T>
+struct S9 {
+ S9() noexcept(noexcept(w)) { }
+ S9 &operator=(S9 &&) noexcept(T::X);
+ W w;
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
@@ -0,0 +1,26 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct S {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S::f1() noexcept // { dg-error "different exception specifier" }
+{
+}
+
+void
+S::f2() // { dg-error "different exception specifier" }
+{
+}
+
+struct S2 {
+ void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
+ void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
+ void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
+ void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
@@ -0,0 +1,9 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+template <typename _Alloc> class A {
+ typedef _Alloc _Alloc_traits;
+ A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ void m_fn2(A<char>) {}
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
@@ -0,0 +1,14 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void fn1(void());
+template <typename> class A {
+ void _M_local_data();
+ A() noexcept(_M_local_data);
+};
+
+class B {
+ void _S_initialize();
+ static void _S_initialize_once();
+};
+void B::_S_initialize() { fn1(_S_initialize_once); }
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
new file mode 100644
index 00000000000..39df4a6571e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
@@ -0,0 +1,23 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+ virtual void f();
+ virtual void g() noexcept;
+ virtual void h() noexcept(false);
+};
+
+struct B : A
+{
+ void f() noexcept(true);
+ void g() noexcept(true);
+ void h() noexcept(true);
+};
+
+struct D : A
+{
+ void f() noexcept(false);
+ void g() noexcept(false); // { dg-error "looser exception specification" }
+ void h() noexcept(false);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
new file mode 100644
index 00000000000..da7490d651c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
@@ -0,0 +1,28 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void f() noexcept(false);
+void g() noexcept(true);
+void h() noexcept;
+
+struct B {
+ friend void f() noexcept(false);
+ friend void g() noexcept(false); // { dg-error "different exception specifier" }
+ friend void h() noexcept(false); // { dg-error "different exception specifier" }
+};
+
+struct C {
+ friend void f() noexcept(true); // { dg-error "different exception specifier" }
+ friend void g() noexcept(true); // { dg-error "different exception specifier" }
+ friend void h() noexcept(true); // { dg-error "different exception specifier" }
+};
+
+void o() noexcept(false);
+void p() noexcept(true);
+void q() noexcept;
+
+struct D {
+ friend void o() noexcept(true); // { dg-error "different exception specifier" }
+ friend void p() noexcept(true);
+ friend void q() noexcept(true);
+};
diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
index 0ba6145ef0c..6bccc704d49 100644
--- gcc/testsuite/g++.dg/eh/shadow1.C
+++ gcc/testsuite/g++.dg/eh/shadow1.C
@@ -18,7 +18,7 @@ struct D : private B
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
struct E : public D
{
- virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
+ virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
}; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
B* foo (D *);
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-05-10 19:21 ` Marek Polacek
@ 2019-05-17 14:35 ` Marek Polacek
2019-05-24 16:17 ` Marek Polacek
2019-05-28 15:48 ` Jason Merrill
1 sibling, 1 reply; 14+ messages in thread
From: Marek Polacek @ 2019-05-17 14:35 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
Ping.
On Fri, May 10, 2019 at 03:21:33PM -0400, Marek Polacek wrote:
> Coming back to this. I didn't think this was suitable for GCC 9.
>
> On Mon, Jan 07, 2019 at 10:44:37AM -0500, Jason Merrill wrote:
> > On 12/19/18 3:27 PM, Marek Polacek wrote:
> > > Prompted by Jon's observation in 52869, I noticed that we don't treat
> > > a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
> > > As with member function bodies, default arguments, and NSDMIs, names used in
> > > a noexcept-specifier of a member-function can be declared later in the class
> > > body, so we need to wait and parse them at the end of the class.
> > > For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
> >
> > Or DEFERRED_PARSE, yes.
>
> I didn't change the name but I'm happy to do it as a follow up.
>
> > > + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> > > + and check this again after we've parsed the noexcept-specifiers
> > > + for real. */
> > > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > > + {
> > > + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
> > > + copy_decl (old_decl));
> > > + return;
> > > + }
> >
> > Why copy_decl?
>
> This is so that we don't lose the diagnostic in noexcept46.C. If I don't use
> copy_decl then the tree is shared and subsequent changes to it make us not
> detect discrepancies like noexcept(false) vs. noexcept(true) on the same decl.
>
> > It seems wasteful to allocate a vec to hold this single decl; let's make the
> > last field of tree_default_arg a union instead. And add a new macro for the
> > single decl case.
>
> Done. But that required also adding GTY markers *and* a new BOOL_BITFIELD for
> the sake of GTY((desc)).
>
> > I notice that default_arg currently uses tree_common for some reason, and we
> > ought to be able to save two words by switching to tree_base
>
> Done.
>
> > > @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> > > || TREE_VALUE (spec)
> > > || spec == noexcept_false_spec
> > > || TREE_PURPOSE (spec) == error_mark_node
> > > + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
> >
> > Maybe use UNPARSED_NOEXCEPT_SPEC_P here?
>
> Done.
>
> > > +/* Make sure that any member-function parameters are in scope.
> > > + For instance, a function's noexcept-specifier can use the function's
> > > + parameters:
> > > +
> > > + struct S {
> > > + void fn (int p) noexcept(noexcept(p));
> > > + };
> > > +
> > > + so we need to make sure name lookup can find them. This is used
> > > + when we delay parsing of the noexcept-specifier. */
> > > +
> > > +static void
> > > +maybe_begin_member_function_processing (tree decl)
> >
> > This name is pretty misleading. How about inject_parm_decls, to go with
> > inject_this_parameter?
>
> Done.
>
> > > +/* Undo the effects of maybe_begin_member_function_processing. */
> > > +
> > > +static void
> > > +maybe_end_member_function_processing (void)
> >
> > And then perhaps pop_injected_parms.
>
> Done.
>
> > > +/* Check throw specifier of OVERRIDER is at least as strict as
> > > + the one of BASEFN. */
> > > +
> > > +bool
> > > +maybe_check_throw_specifier (tree overrider, tree basefn)
> > > +{
> > > + maybe_instantiate_noexcept (basefn);
> > > + maybe_instantiate_noexcept (overrider);
> > > + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> > > + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> > > +
> > > + if (DECL_INVALID_OVERRIDER_P (overrider))
> > > + return true;
> > > +
> > > + /* Can't check this yet. Pretend this is fine and let
> > > + noexcept_override_late_checks check this later. */
> > > + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> > > + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> > > + return true;
> > > +
> > > + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> > > + {
> > > + auto_diagnostic_group d;
> > > + error ("looser throw specifier for %q+#F", overrider);
> >
> > Since we're touching this diagnostic, let's correct it now to "exception
> > specification". And add "on overriding virtual function".
>
> Ok, changed to the more up-to-date term.
>
> Two further changes were required since my changes to detecting 'this' for
> static member functions:
> 1) use THIS_FORBIDDEN if needed when parsing the delayed noexcept,
> 2) careful about friend member functions -- its DECL_CONTEXT is not the
> containing class, need to use DECL_FRIEND_CONTEXT.
>
> Both of these points are tested in g++.dg/cpp0x/this1.C.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2019-05-10 Marek Polacek <polacek@redhat.com>
>
> PR c++/86476 - noexcept-specifier is a complete-class context.
> PR c++/52869
> * cp-tree.def (DEFAULT_ARG): Update commentary.
> * cp-tree.h (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P):
> New macros.
> (tree_default_arg): Add a tree field, make the last two fields into a
> union. Add GTY markers.
> (check_redeclaration_exception_specification): Declare.
> (maybe_check_throw_specifier): Declare.
> * decl.c (check_redeclaration_exception_specification): No longer
> static. Handle UNPARSED_NOEXCEPT_SPEC_P.
> * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
> * parser.c (cp_parser_noexcept_specification_opt,
> cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
> Forward-declare.
> (unparsed_noexcepts): New macro.
> (push_unparsed_function_queues): Update initializer.
> (cp_parser_init_declarator): Maybe save the noexcept-specifier to
> post process.
> (inject_parm_decls): New.
> (pop_injected_parms): New.
> (cp_parser_class_specifier_1): Implement delayed parsing of
> noexcept-specifiers.
> (cp_parser_member_declaration): Maybe save the noexcept-specifier to
> post process.
> (cp_parser_save_noexcept): New.
> (cp_parser_late_noexcept_specifier): New.
> (noexcept_override_late_checks): New.
> (cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept
> instead of the normal processing if needed.
> (cp_parser_save_member_function_body): Maybe save the
> noexcept-specifier to post process.
> * parser.h (cp_unparsed_functions_entry): Add new field to carry
> a noexcept-specifier.
> * pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
> * search.c (maybe_check_throw_specifier): New function, broken out
> of...
> (check_final_overrider): ...here. Call maybe_check_throw_specifier.
> * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
> (cp_tree_equal): Handle DEFAULT_ARG.
> * typeck2.c (merge_exception_specifiers): If an unparsed noexcept
> expression has been passed, return it instead of merging it.
>
> * g++.dg/cpp0x/noexcept41.C: New test.
> * g++.dg/cpp0x/noexcept42.C: New test.
> * g++.dg/cpp0x/noexcept43.C: New test.
> * g++.dg/cpp0x/noexcept44.C: New test.
> * g++.dg/cpp0x/noexcept45.C: New test.
> * g++.dg/cpp0x/noexcept46.C: New test.
> * g++.dg/eh/shadow1.C: Adjust dg-error.
>
> diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> index 03c105b5c4c..33eb5d25efe 100644
> --- gcc/cp/cp-tree.def
> +++ gcc/cp/cp-tree.def
> @@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
>
> /* An un-parsed default argument. Holds a vector of input tokens and
> a vector of places where the argument was instantiated before
> - parsing had occurred. */
> + parsing had occurred. This is also used for delayed NSDMIs and
> + noexcept-specifier parsing. For a noexcept-specifier, we use a tree
> + holding a function declaration used for late checking. */
> DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
>
> /* An uninstantiated/unevaluated noexcept-specification. For the
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index f253857b02a..ef14a011293 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -1178,12 +1178,20 @@ enum cp_identifier_kind {
> #define DEFARG_TOKENS(NODE) \
> (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens)
> #define DEFARG_INSTANTIATIONS(NODE) \
> - (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
> + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations)
> +#define DEFARG_DECL(NODE) \
> + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl)
> +#define DEFARG_NOEXCEPT_P(NODE) \
> + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p)
>
> struct GTY (()) tree_default_arg {
> - struct tree_common common;
> + struct tree_base base;
> struct cp_token_cache *tokens;
> - vec<tree, va_gc> *instantiations;
> + BOOL_BITFIELD noexcept_p : 1;
> + union {
> + vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
> + tree GTY((tag ("1"))) decl;
> + } GTY((desc ("%1.noexcept_p"))) u;
> };
>
>
> @@ -1197,6 +1205,9 @@ struct GTY (()) tree_default_arg {
> #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
> (DEFERRED_NOEXCEPT_SPEC_P (NODE) \
> && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
> +#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
> + ((NODE) && (TREE_PURPOSE (NODE)) \
> + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
>
> struct GTY (()) tree_deferred_noexcept {
> struct tree_base base;
> @@ -6464,6 +6475,8 @@ extern bool check_array_designated_initializer (constructor_elt *,
> unsigned HOST_WIDE_INT);
> extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
> extern tree build_explicit_specifier (tree, tsubst_flags_t);
> +extern void check_redeclaration_exception_specification
> + (tree, tree);
>
> /* in decl2.c */
> extern void record_mangling (tree, bool);
> @@ -6929,6 +6942,7 @@ extern tree copied_binfo (tree, tree);
> extern tree original_binfo (tree, tree);
> extern int shared_member_p (tree);
> extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
> +extern bool maybe_check_throw_specifier (tree, tree);
>
> /* The representation of a deferred access check. */
>
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index 36014dc628e..a2effa13623 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
> function templates. If their exception specifications do not
> match, issue a diagnostic. */
>
> -static void
> +void
> check_redeclaration_exception_specification (tree new_decl,
> tree old_decl)
> {
> @@ -1151,6 +1151,15 @@ check_redeclaration_exception_specification (tree new_decl,
> && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
> return;
>
> + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> + and check this again after we've parsed the noexcept-specifiers
> + for real. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> + {
> + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> + return;
> + }
> +
> if (!type_dependent_expression_p (old_decl))
> {
> maybe_instantiate_noexcept (new_decl);
> diff --git gcc/cp/except.c gcc/cp/except.c
> index afc261073d7..208c9c1461d 100644
> --- gcc/cp/except.c
> +++ gcc/cp/except.c
> @@ -1248,6 +1248,7 @@ nothrow_spec_p (const_tree spec)
> || TREE_VALUE (spec)
> || spec == noexcept_false_spec
> || TREE_PURPOSE (spec) == error_mark_node
> + || UNPARSED_NOEXCEPT_SPEC_P (spec)
> || processing_template_decl);
>
> return false;
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index 12beadf5096..41197ab3486 100644
> --- gcc/cp/parser.c
> +++ gcc/cp/parser.c
> @@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
>
> static cp_token_cache *cp_token_cache_new
> (cp_token *, cp_token *);
> +static tree cp_parser_noexcept_specification_opt
> + (cp_parser *, bool, bool *, bool);
> +static tree cp_parser_late_noexcept_specifier
> + (cp_parser *, tree);
> +static void noexcept_override_late_checks
> + (tree, tree);
>
> static void cp_parser_initial_pragma
> (cp_token *);
> @@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
> parser->unparsed_queues->last ().nsdmis
> #define unparsed_classes \
> parser->unparsed_queues->last ().classes
> +#define unparsed_noexcepts \
> + parser->unparsed_queues->last ().noexcepts
>
> static void
> push_unparsed_function_queues (cp_parser *parser)
> {
> - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
> + cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
> + NULL };
> vec_safe_push (parser->unparsed_queues, e);
> }
>
> @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
> /*asmspec=*/NULL_TREE,
> attr_chainon (attributes, prefix_attributes));
> if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> - cp_parser_save_default_args (parser, decl);
> + {
> + cp_parser_save_default_args (parser, decl);
> + /* Remember if there is a noexcept-specifier to post process. */
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, decl);
> + }
> cp_finalize_omp_declare_simd (parser, decl);
> cp_finalize_oacc_routine (parser, decl, false);
> }
> @@ -23334,6 +23349,45 @@ cp_parser_class_name (cp_parser *parser,
> return decl;
> }
>
> +/* Make sure that any member-function parameters are in scope.
> + For instance, a function's noexcept-specifier can use the function's
> + parameters:
> +
> + struct S {
> + void fn (int p) noexcept(noexcept(p));
> + };
> +
> + so we need to make sure name lookup can find them. This is used
> + when we delay parsing of the noexcept-specifier. */
> +
> +static void
> +inject_parm_decls (tree decl)
> +{
> + begin_scope (sk_function_parms, decl);
> + tree args = DECL_ARGUMENTS (decl);
> + args = nreverse (args);
> +
> + tree next;
> + for (tree parm = args; parm; parm = next)
> + {
> + next = DECL_CHAIN (parm);
> + if (TREE_CODE (parm) == PARM_DECL)
> + pushdecl (parm);
> + }
> + /* Get the decls in their original chain order and record in the
> + function. This is all and only the PARM_DECLs that were
> + pushed into scope by the loop above. */
> + DECL_ARGUMENTS (decl) = get_local_decls ();
> +}
> +
> +/* Undo the effects of inject_parm_decls. */
> +
> +static void
> +pop_injected_parms (void)
> +{
> + pop_bindings_and_leave_scope ();
> +}
> +
> /* Parse a class-specifier.
>
> class-specifier:
> @@ -23644,6 +23698,66 @@ cp_parser_class_specifier_1 (cp_parser* parser)
> vec_safe_truncate (unparsed_classes, 0);
> after_nsdmi_defaulted_late_checks (type);
>
> + /* If there are noexcept-specifiers that have not yet been processed,
> + take care of them now. */
> + class_type = NULL_TREE;
> + pushed_scope = NULL_TREE;
> + FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
> + {
> + tree ctx = (DECL_FRIEND_P (decl) ? DECL_FRIEND_CONTEXT (decl)
> + : DECL_CONTEXT (decl));
> + if (class_type != ctx)
> + {
> + if (pushed_scope)
> + pop_scope (pushed_scope);
> + class_type = ctx;
> + pushed_scope = push_scope (class_type);
> + }
> +
> + /* Make sure that any template parameters are in scope. */
> + maybe_begin_member_template_processing (decl);
> +
> + /* Make sure that any member-function parameters are in scope. */
> + inject_parm_decls (decl);
> +
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + spec = TREE_PURPOSE (spec);
> + tree old_decl = DEFARG_DECL (spec);
> +
> + /* 'this' is not allowed in static member functions. */
> + unsigned char local_variables_forbidden_p
> + = parser->local_variables_forbidden_p;
> + if (DECL_THIS_STATIC (decl))
> + parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
> +
> + /* Now we can parse the noexcept-specifier. */
> + spec = cp_parser_late_noexcept_specifier (parser, spec);
> +
> + /* Restore the state of local_variables_forbidden_p. */
> + parser->local_variables_forbidden_p = local_variables_forbidden_p;
> + if (spec != error_mark_node)
> + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
> +
> + /* If we've stashed an old declaration, it means we need to
> + perform late redeclaration checking. */
> + if (old_decl)
> + check_redeclaration_exception_specification (decl, old_decl);
> +
> + /* The finish_struct call above performed various override checking,
> + but it skipped unparsed noexcept-specifier operands. Now that we
> + have resolved them, check again. */
> + noexcept_override_late_checks (type, decl);
> +
> + /* Remove any member-function parameters from the symbol table. */
> + pop_injected_parms ();
> +
> + /* Remove any template parameters from the symbol table. */
> + maybe_end_member_template_processing ();
> + }
> + vec_safe_truncate (unparsed_noexcepts, 0);
> + if (pushed_scope)
> + pop_scope (pushed_scope);
> +
> /* Now parse the body of the functions. */
> if (flag_openmp)
> {
> @@ -24817,6 +24931,12 @@ cp_parser_member_declaration (cp_parser* parser)
> else
> decl = finish_fully_implicit_template (parser, decl);
> }
> + if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> + {
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, decl);
> + }
> }
>
> cp_finalize_omp_declare_simd (parser, decl);
> @@ -25199,6 +25319,90 @@ cp_parser_base_specifier (cp_parser* parser)
>
> /* Exception handling [gram.exception] */
>
> +/* Save the tokens that make up the noexcept-specifier for a member-function.
> + Returns a DEFAULT_ARG. */
> +
> +static tree
> +cp_parser_save_noexcept (cp_parser *parser)
> +{
> + cp_token *first = parser->lexer->next_token;
> + /* We want everything up to, including, the final ')'. */
> + cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
> + cp_token *last = parser->lexer->next_token;
> +
> + /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
> + to carry the information we will need. */
> + tree expr = make_node (DEFAULT_ARG);
> + /* Save away the noexcept-specifier; we will process it when the
> + class is complete. */
> + DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
> + DEFARG_DECL (expr) = NULL_TREE;
> + DEFARG_NOEXCEPT_P (expr) = true;
> + expr = build_tree_list (expr, NULL_TREE);
> + return expr;
> +}
> +
> +/* Used for late processing of noexcept-specifiers of member-functions.
> + DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
> + we saved for later; parse it now. */
> +
> +static tree
> +cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
> +{
> + /* Make sure we've gotten something that hasn't been parsed yet. */
> + gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
> +
> + push_unparsed_function_queues (parser);
> +
> + /* Push the saved tokens for the noexcept-specifier onto the parser's
> + lexer stack. */
> + cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
> + cp_parser_push_lexer_for_tokens (parser, tokens);
> +
> + /* Parse the cached noexcept-specifier. */
> + tree parsed_arg
> + = cp_parser_noexcept_specification_opt (parser,
> + /*require_constexpr=*/true,
> + NULL,
> + /*return_cond=*/false);
> +
> + /* Revert to the main lexer. */
> + cp_parser_pop_lexer (parser);
> +
> + /* Restore the queue. */
> + pop_unparsed_function_queues (parser);
> +
> + /* And we're done. */
> + return parsed_arg;
> +}
> +
> +/* Perform late checking of overriding function with respect to their
> + noexcept-specifiers. TYPE is the class and FNDECL is the function
> + that potentially overrides some virtual function with the same
> + signature. */
> +
> +static void
> +noexcept_override_late_checks (tree type, tree fndecl)
> +{
> + tree binfo = TYPE_BINFO (type);
> + tree base_binfo;
> +
> + if (DECL_STATIC_FUNCTION_P (fndecl))
> + return;
> +
> + for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
> + {
> + tree basetype = BINFO_TYPE (base_binfo);
> +
> + if (!TYPE_POLYMORPHIC_P (basetype))
> + continue;
> +
> + tree fn = look_for_overrides_here (basetype, fndecl);
> + if (fn)
> + maybe_check_throw_specifier (fndecl, fn);
> + }
> +}
> +
> /* Parse an (optional) noexcept-specification.
>
> noexcept-specification:
> @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> if (cp_parser_is_keyword (token, RID_NOEXCEPT))
> {
> tree expr;
> +
> + /* [class.mem]/6 says that a noexcept-specifer (within the
> + member-specification of the class) is a complete-class context of
> + a class. So, if the noexcept-specifier has the optional expression,
> + just save the tokens, and reparse this after we're done with the
> + class. */
> + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
> + && at_class_scope_p ()
> + && TYPE_BEING_DEFINED (current_class_type)
> + && !LAMBDA_TYPE_P (current_class_type))
> + return cp_parser_save_noexcept (parser);
> +
> cp_lexer_consume_token (parser->lexer);
>
> if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
> @@ -28427,7 +28643,12 @@ cp_parser_save_member_function_body (cp_parser* parser,
> return error_mark_node;
> }
>
> - /* Remember it, if there default args to post process. */
> + /* Remember if there is a noexcept-specifier to post process. */
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, fn);
> +
> + /* Remember it, if there are default args to post process. */
> cp_parser_save_default_args (parser, fn);
>
> /* Save away the tokens that make up the body of the
> diff --git gcc/cp/parser.h gcc/cp/parser.h
> index c03a9d87af5..2890788f489 100644
> --- gcc/cp/parser.h
> +++ gcc/cp/parser.h
> @@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
> /* Nested classes go in this vector, so that we can do some final
> processing after parsing any NSDMIs. */
> vec<tree, va_gc> *classes;
> +
> + /* Functions with noexcept-specifiers that require post-processing. */
> + vec<tree, va_gc> *noexcepts;
> };
>
>
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index d6976e08690..c00d14fd954 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -25308,8 +25308,9 @@ dependent_type_p_r (tree type)
> if (tree noex = TREE_PURPOSE (spec))
> /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
> affect overload resolution and treating it as dependent breaks
> - things. */
> + things. Same for an unparsed noexcept expression. */
> if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
> + && TREE_CODE (noex) != DEFAULT_ARG
> && value_dependent_expression_p (noex))
> return true;
> return false;
> diff --git gcc/cp/search.c gcc/cp/search.c
> index 4c3fffda717..5a3a0cf2824 100644
> --- gcc/cp/search.c
> +++ gcc/cp/search.c
> @@ -1863,6 +1863,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
> NULL, &lfd);
> }
>
> +/* Check throw specifier of OVERRIDER is at least as strict as
> + the one of BASEFN. */
> +
> +bool
> +maybe_check_throw_specifier (tree overrider, tree basefn)
> +{
> + maybe_instantiate_noexcept (basefn);
> + maybe_instantiate_noexcept (overrider);
> + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> +
> + if (DECL_INVALID_OVERRIDER_P (overrider))
> + return true;
> +
> + /* Can't check this yet. Pretend this is fine and let
> + noexcept_override_late_checks check this later. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> + return true;
> +
> + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> + {
> + auto_diagnostic_group d;
> + error ("looser exception specification on overriding virtual function "
> + "%q+#F", overrider);
> + inform (DECL_SOURCE_LOCATION (basefn),
> + "overridden function is %q#F", basefn);
> + DECL_INVALID_OVERRIDER_P (overrider) = 1;
> + return false;
> + }
> + return true;
> +}
> +
> /* Check that virtual overrider OVERRIDER is acceptable for base function
> BASEFN. Issue diagnostic, and return zero, if unacceptable. */
>
> @@ -1873,7 +1906,6 @@ check_final_overrider (tree overrider, tree basefn)
> tree base_type = TREE_TYPE (basefn);
> tree over_return = fndecl_declared_return_type (overrider);
> tree base_return = fndecl_declared_return_type (basefn);
> - tree over_throw, base_throw;
>
> int fail = 0;
>
> @@ -1957,21 +1989,8 @@ check_final_overrider (tree overrider, tree basefn)
> return 0;
> }
>
> - /* Check throw specifier is at least as strict. */
> - maybe_instantiate_noexcept (basefn);
> - maybe_instantiate_noexcept (overrider);
> - base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> - over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> -
> - if (!comp_except_specs (base_throw, over_throw, ce_derived))
> - {
> - auto_diagnostic_group d;
> - error ("looser throw specifier for %q+#F", overrider);
> - inform (DECL_SOURCE_LOCATION (basefn),
> - "overridden function is %q#F", basefn);
> - DECL_INVALID_OVERRIDER_P (overrider) = 1;
> - return 0;
> - }
> + if (!maybe_check_throw_specifier (overrider, basefn))
> + return 0;
>
> /* Check for conflicting type attributes. But leave transaction_safe for
> set_one_vmethod_tm_attributes. */
> diff --git gcc/cp/tree.c gcc/cp/tree.c
> index 718eed349c6..bc0080d6720 100644
> --- gcc/cp/tree.c
> +++ gcc/cp/tree.c
> @@ -2550,6 +2550,7 @@ canonical_eh_spec (tree raises)
> if (raises == NULL_TREE)
> return raises;
> else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
> + || UNPARSED_NOEXCEPT_SPEC_P (raises)
> || uses_template_parms (raises)
> || uses_template_parms (TREE_PURPOSE (raises)))
> /* Keep a dependent or deferred exception specification. */
> @@ -3662,6 +3663,7 @@ cp_tree_equal (tree t1, tree t2)
> case IDENTIFIER_NODE:
> case SSA_NAME:
> case USING_DECL:
> + case DEFAULT_ARG:
> return false;
>
> case BASELINK:
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index df002a1664c..8cbc48fb44f 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
> if (list == error_mark_node || add == error_mark_node)
> return error_mark_node;
>
> + /* We don't want to lose the unparsed operand lest we miss diagnostics. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> + return list;
> + else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> + return add;
> +
> /* No exception-specifier or noexcept(false) are less strict than
> anything else. Prefer the newer variant (LIST). */
> if (!list || list == noexcept_false_spec)
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> new file mode 100644
> index 00000000000..43b38c2446f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> @@ -0,0 +1,147 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert(X, #X)
> +
> +struct S {
> + void f1() noexcept(noexcept(i)) { }
> + void f2() noexcept(noexcept(fn())) { }
> + void f3() noexcept(noexcept(fnx())) { }
> + void f4() noexcept(noexcept(i));
> + void f5() noexcept(noexcept(fn()));
> + void f6() noexcept(noexcept(fnx()));
> +
> + void f7() noexcept(1);
> + void f8() noexcept(0);
> + void f9() noexcept(b);
> + void f10() noexcept(!b);
> +
> + int i;
> + static constexpr auto b = true;
> + void fny() noexcept(noexcept(fn()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +S s;
> +SA(noexcept(s.f1()));
> +SA(!noexcept(s.f2()));
> +SA(noexcept(s.f3()));
> +SA(noexcept(s.f4()));
> +SA(!noexcept(s.f5()));
> +SA(noexcept(s.f6()));
> +SA(noexcept(s.f7()));
> +SA(!noexcept(s.f8()));
> +SA(noexcept(s.f9()));
> +SA(!noexcept(s.f10()));
> +
> +struct S2 {
> + struct V {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void f3() noexcept(noexcept(fn())) { }
> + void f4() noexcept(noexcept(fnx())) { }
> + void fn();
> + void fnx() noexcept;
> + } v;
> + void fn();
> + void fnx();
> +};
> +
> +S2 s2;
> +SA(!noexcept(s2.v.f1()));
> +SA(noexcept(s2.v.f2()));
> +SA(!noexcept(s2.v.f3()));
> +SA(noexcept(s2.v.f4()));
> +
> +struct S3 {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +void
> +S3::f1() noexcept(noexcept(fn()))
> +{
> +}
> +
> +void
> +S3::f2() noexcept(noexcept(fnx()))
> +{
> +}
> +
> +struct S4 {
> + int f1 (int p) noexcept(noexcept(p)) { return p; }
> + int f2 (int p) noexcept(noexcept(p));
> + int f3 (int p = 10) noexcept(noexcept(p));
> + int f4 () noexcept(noexcept(S4{}));
> +};
> +
> +S4 s4;
> +SA(noexcept(s4.f1(1)));
> +SA(noexcept(s4.f2(1)));
> +SA(noexcept(s4.f3()));
> +SA(noexcept(s4.f4()));
> +
> +template<typename T>
> +struct S5 {
> + void f1() noexcept(noexcept(i)) { }
> + void f2() noexcept(noexcept(fn())) { }
> + void f3() noexcept(noexcept(fnx())) { }
> + void f4() noexcept(noexcept(i));
> + void f5() noexcept(noexcept(fn()));
> + void f6() noexcept(noexcept(fnx()));
> +
> + int i;
> + void fny() noexcept(noexcept(fn()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +S5<int> s5;
> +SA(noexcept(s5.f1()));
> +SA(!noexcept(s5.f2()));
> +SA(noexcept(s5.f3()));
> +SA(noexcept(s5.f4()));
> +SA(!noexcept(s5.f5()));
> +SA(noexcept(s5.f6()));
> +
> +template<typename T>
> +struct S6 {
> + void f1() noexcept(noexcept(x));
> + T x;
> +};
> +
> +struct S7 {
> + template<typename U>
> + void f1 () noexcept(noexcept(U(1))) { }
> +
> + template<int N>
> + void f2() noexcept(noexcept(N));
> +
> + template <typename _Up>
> + void f3(_Up __p) noexcept(noexcept(__p));
> +};
> +
> +void glob();
> +void globx() noexcept;
> +struct S8 {
> + void f1 () noexcept(noexcept(glob()));
> + void f2 () noexcept(noexcept(globx()));
> +};
> +
> +S8 s8;
> +SA(!noexcept(s8.f1()));
> +SA(noexcept(s8.f2()));
> +
> +struct W {
> + constexpr operator bool();
> +};
> +
> +template<typename T>
> +struct S9 {
> + S9() noexcept(noexcept(w)) { }
> + S9 &operator=(S9 &&) noexcept(T::X);
> + W w;
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> new file mode 100644
> index 00000000000..b3859de9ebc
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> @@ -0,0 +1,26 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +struct S {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +void
> +S::f1() noexcept // { dg-error "different exception specifier" }
> +{
> +}
> +
> +void
> +S::f2() // { dg-error "different exception specifier" }
> +{
> +}
> +
> +struct S2 {
> + void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
> + void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
> + void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
> + void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> new file mode 100644
> index 00000000000..12c6d364099
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> @@ -0,0 +1,9 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +template <typename _Alloc> class A {
> + typedef _Alloc _Alloc_traits;
> + A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> + A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> + void m_fn2(A<char>) {}
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> new file mode 100644
> index 00000000000..a81032f28e9
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> @@ -0,0 +1,14 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +void fn1(void());
> +template <typename> class A {
> + void _M_local_data();
> + A() noexcept(_M_local_data);
> +};
> +
> +class B {
> + void _S_initialize();
> + static void _S_initialize_once();
> +};
> +void B::_S_initialize() { fn1(_S_initialize_once); }
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> new file mode 100644
> index 00000000000..39df4a6571e
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> @@ -0,0 +1,23 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +struct A
> +{
> + virtual void f();
> + virtual void g() noexcept;
> + virtual void h() noexcept(false);
> +};
> +
> +struct B : A
> +{
> + void f() noexcept(true);
> + void g() noexcept(true);
> + void h() noexcept(true);
> +};
> +
> +struct D : A
> +{
> + void f() noexcept(false);
> + void g() noexcept(false); // { dg-error "looser exception specification" }
> + void h() noexcept(false);
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> new file mode 100644
> index 00000000000..da7490d651c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> @@ -0,0 +1,28 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +void f() noexcept(false);
> +void g() noexcept(true);
> +void h() noexcept;
> +
> +struct B {
> + friend void f() noexcept(false);
> + friend void g() noexcept(false); // { dg-error "different exception specifier" }
> + friend void h() noexcept(false); // { dg-error "different exception specifier" }
> +};
> +
> +struct C {
> + friend void f() noexcept(true); // { dg-error "different exception specifier" }
> + friend void g() noexcept(true); // { dg-error "different exception specifier" }
> + friend void h() noexcept(true); // { dg-error "different exception specifier" }
> +};
> +
> +void o() noexcept(false);
> +void p() noexcept(true);
> +void q() noexcept;
> +
> +struct D {
> + friend void o() noexcept(true); // { dg-error "different exception specifier" }
> + friend void p() noexcept(true);
> + friend void q() noexcept(true);
> +};
> diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
> index 0ba6145ef0c..6bccc704d49 100644
> --- gcc/testsuite/g++.dg/eh/shadow1.C
> +++ gcc/testsuite/g++.dg/eh/shadow1.C
> @@ -18,7 +18,7 @@ struct D : private B
> // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
> struct E : public D
> {
> - virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
> + virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
> }; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
> // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
> B* foo (D *);
Marek
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-05-17 14:35 ` Marek Polacek
@ 2019-05-24 16:17 ` Marek Polacek
0 siblings, 0 replies; 14+ messages in thread
From: Marek Polacek @ 2019-05-24 16:17 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
Ping.
On Fri, May 17, 2019 at 10:35:29AM -0400, Marek Polacek wrote:
> Ping.
>
> On Fri, May 10, 2019 at 03:21:33PM -0400, Marek Polacek wrote:
> > Coming back to this. I didn't think this was suitable for GCC 9.
> >
> > On Mon, Jan 07, 2019 at 10:44:37AM -0500, Jason Merrill wrote:
> > > On 12/19/18 3:27 PM, Marek Polacek wrote:
> > > > Prompted by Jon's observation in 52869, I noticed that we don't treat
> > > > a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
> > > > As with member function bodies, default arguments, and NSDMIs, names used in
> > > > a noexcept-specifier of a member-function can be declared later in the class
> > > > body, so we need to wait and parse them at the end of the class.
> > > > For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
> > >
> > > Or DEFERRED_PARSE, yes.
> >
> > I didn't change the name but I'm happy to do it as a follow up.
> >
> > > > + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> > > > + and check this again after we've parsed the noexcept-specifiers
> > > > + for real. */
> > > > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > > > + {
> > > > + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
> > > > + copy_decl (old_decl));
> > > > + return;
> > > > + }
> > >
> > > Why copy_decl?
> >
> > This is so that we don't lose the diagnostic in noexcept46.C. If I don't use
> > copy_decl then the tree is shared and subsequent changes to it make us not
> > detect discrepancies like noexcept(false) vs. noexcept(true) on the same decl.
> >
> > > It seems wasteful to allocate a vec to hold this single decl; let's make the
> > > last field of tree_default_arg a union instead. And add a new macro for the
> > > single decl case.
> >
> > Done. But that required also adding GTY markers *and* a new BOOL_BITFIELD for
> > the sake of GTY((desc)).
> >
> > > I notice that default_arg currently uses tree_common for some reason, and we
> > > ought to be able to save two words by switching to tree_base
> >
> > Done.
> >
> > > > @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> > > > || TREE_VALUE (spec)
> > > > || spec == noexcept_false_spec
> > > > || TREE_PURPOSE (spec) == error_mark_node
> > > > + || TREE_CODE (TREE_PURPOSE (spec)) == DEFAULT_ARG
> > >
> > > Maybe use UNPARSED_NOEXCEPT_SPEC_P here?
> >
> > Done.
> >
> > > > +/* Make sure that any member-function parameters are in scope.
> > > > + For instance, a function's noexcept-specifier can use the function's
> > > > + parameters:
> > > > +
> > > > + struct S {
> > > > + void fn (int p) noexcept(noexcept(p));
> > > > + };
> > > > +
> > > > + so we need to make sure name lookup can find them. This is used
> > > > + when we delay parsing of the noexcept-specifier. */
> > > > +
> > > > +static void
> > > > +maybe_begin_member_function_processing (tree decl)
> > >
> > > This name is pretty misleading. How about inject_parm_decls, to go with
> > > inject_this_parameter?
> >
> > Done.
> >
> > > > +/* Undo the effects of maybe_begin_member_function_processing. */
> > > > +
> > > > +static void
> > > > +maybe_end_member_function_processing (void)
> > >
> > > And then perhaps pop_injected_parms.
> >
> > Done.
> >
> > > > +/* Check throw specifier of OVERRIDER is at least as strict as
> > > > + the one of BASEFN. */
> > > > +
> > > > +bool
> > > > +maybe_check_throw_specifier (tree overrider, tree basefn)
> > > > +{
> > > > + maybe_instantiate_noexcept (basefn);
> > > > + maybe_instantiate_noexcept (overrider);
> > > > + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> > > > + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> > > > +
> > > > + if (DECL_INVALID_OVERRIDER_P (overrider))
> > > > + return true;
> > > > +
> > > > + /* Can't check this yet. Pretend this is fine and let
> > > > + noexcept_override_late_checks check this later. */
> > > > + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> > > > + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> > > > + return true;
> > > > +
> > > > + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> > > > + {
> > > > + auto_diagnostic_group d;
> > > > + error ("looser throw specifier for %q+#F", overrider);
> > >
> > > Since we're touching this diagnostic, let's correct it now to "exception
> > > specification". And add "on overriding virtual function".
> >
> > Ok, changed to the more up-to-date term.
> >
> > Two further changes were required since my changes to detecting 'this' for
> > static member functions:
> > 1) use THIS_FORBIDDEN if needed when parsing the delayed noexcept,
> > 2) careful about friend member functions -- its DECL_CONTEXT is not the
> > containing class, need to use DECL_FRIEND_CONTEXT.
> >
> > Both of these points are tested in g++.dg/cpp0x/this1.C.
> >
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> >
> > 2019-05-10 Marek Polacek <polacek@redhat.com>
> >
> > PR c++/86476 - noexcept-specifier is a complete-class context.
> > PR c++/52869
> > * cp-tree.def (DEFAULT_ARG): Update commentary.
> > * cp-tree.h (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P):
> > New macros.
> > (tree_default_arg): Add a tree field, make the last two fields into a
> > union. Add GTY markers.
> > (check_redeclaration_exception_specification): Declare.
> > (maybe_check_throw_specifier): Declare.
> > * decl.c (check_redeclaration_exception_specification): No longer
> > static. Handle UNPARSED_NOEXCEPT_SPEC_P.
> > * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
> > * parser.c (cp_parser_noexcept_specification_opt,
> > cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
> > Forward-declare.
> > (unparsed_noexcepts): New macro.
> > (push_unparsed_function_queues): Update initializer.
> > (cp_parser_init_declarator): Maybe save the noexcept-specifier to
> > post process.
> > (inject_parm_decls): New.
> > (pop_injected_parms): New.
> > (cp_parser_class_specifier_1): Implement delayed parsing of
> > noexcept-specifiers.
> > (cp_parser_member_declaration): Maybe save the noexcept-specifier to
> > post process.
> > (cp_parser_save_noexcept): New.
> > (cp_parser_late_noexcept_specifier): New.
> > (noexcept_override_late_checks): New.
> > (cp_parser_noexcept_specification_opt): Call cp_parser_save_noexcept
> > instead of the normal processing if needed.
> > (cp_parser_save_member_function_body): Maybe save the
> > noexcept-specifier to post process.
> > * parser.h (cp_unparsed_functions_entry): Add new field to carry
> > a noexcept-specifier.
> > * pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
> > * search.c (maybe_check_throw_specifier): New function, broken out
> > of...
> > (check_final_overrider): ...here. Call maybe_check_throw_specifier.
> > * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
> > (cp_tree_equal): Handle DEFAULT_ARG.
> > * typeck2.c (merge_exception_specifiers): If an unparsed noexcept
> > expression has been passed, return it instead of merging it.
> >
> > * g++.dg/cpp0x/noexcept41.C: New test.
> > * g++.dg/cpp0x/noexcept42.C: New test.
> > * g++.dg/cpp0x/noexcept43.C: New test.
> > * g++.dg/cpp0x/noexcept44.C: New test.
> > * g++.dg/cpp0x/noexcept45.C: New test.
> > * g++.dg/cpp0x/noexcept46.C: New test.
> > * g++.dg/eh/shadow1.C: Adjust dg-error.
> >
> > diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> > index 03c105b5c4c..33eb5d25efe 100644
> > --- gcc/cp/cp-tree.def
> > +++ gcc/cp/cp-tree.def
> > @@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
> >
> > /* An un-parsed default argument. Holds a vector of input tokens and
> > a vector of places where the argument was instantiated before
> > - parsing had occurred. */
> > + parsing had occurred. This is also used for delayed NSDMIs and
> > + noexcept-specifier parsing. For a noexcept-specifier, we use a tree
> > + holding a function declaration used for late checking. */
> > DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
> >
> > /* An uninstantiated/unevaluated noexcept-specification. For the
> > diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> > index f253857b02a..ef14a011293 100644
> > --- gcc/cp/cp-tree.h
> > +++ gcc/cp/cp-tree.h
> > @@ -1178,12 +1178,20 @@ enum cp_identifier_kind {
> > #define DEFARG_TOKENS(NODE) \
> > (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens)
> > #define DEFARG_INSTANTIATIONS(NODE) \
> > - (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
> > + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations)
> > +#define DEFARG_DECL(NODE) \
> > + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl)
> > +#define DEFARG_NOEXCEPT_P(NODE) \
> > + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p)
> >
> > struct GTY (()) tree_default_arg {
> > - struct tree_common common;
> > + struct tree_base base;
> > struct cp_token_cache *tokens;
> > - vec<tree, va_gc> *instantiations;
> > + BOOL_BITFIELD noexcept_p : 1;
> > + union {
> > + vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
> > + tree GTY((tag ("1"))) decl;
> > + } GTY((desc ("%1.noexcept_p"))) u;
> > };
> >
> >
> > @@ -1197,6 +1205,9 @@ struct GTY (()) tree_default_arg {
> > #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
> > (DEFERRED_NOEXCEPT_SPEC_P (NODE) \
> > && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
> > +#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
> > + ((NODE) && (TREE_PURPOSE (NODE)) \
> > + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
> >
> > struct GTY (()) tree_deferred_noexcept {
> > struct tree_base base;
> > @@ -6464,6 +6475,8 @@ extern bool check_array_designated_initializer (constructor_elt *,
> > unsigned HOST_WIDE_INT);
> > extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
> > extern tree build_explicit_specifier (tree, tsubst_flags_t);
> > +extern void check_redeclaration_exception_specification
> > + (tree, tree);
> >
> > /* in decl2.c */
> > extern void record_mangling (tree, bool);
> > @@ -6929,6 +6942,7 @@ extern tree copied_binfo (tree, tree);
> > extern tree original_binfo (tree, tree);
> > extern int shared_member_p (tree);
> > extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
> > +extern bool maybe_check_throw_specifier (tree, tree);
> >
> > /* The representation of a deferred access check. */
> >
> > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > index 36014dc628e..a2effa13623 100644
> > --- gcc/cp/decl.c
> > +++ gcc/cp/decl.c
> > @@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
> > function templates. If their exception specifications do not
> > match, issue a diagnostic. */
> >
> > -static void
> > +void
> > check_redeclaration_exception_specification (tree new_decl,
> > tree old_decl)
> > {
> > @@ -1151,6 +1151,15 @@ check_redeclaration_exception_specification (tree new_decl,
> > && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
> > return;
> >
> > + /* We can't compare unparsed noexcept-specifiers. Save the old decl
> > + and check this again after we've parsed the noexcept-specifiers
> > + for real. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > + {
> > + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> > + return;
> > + }
> > +
> > if (!type_dependent_expression_p (old_decl))
> > {
> > maybe_instantiate_noexcept (new_decl);
> > diff --git gcc/cp/except.c gcc/cp/except.c
> > index afc261073d7..208c9c1461d 100644
> > --- gcc/cp/except.c
> > +++ gcc/cp/except.c
> > @@ -1248,6 +1248,7 @@ nothrow_spec_p (const_tree spec)
> > || TREE_VALUE (spec)
> > || spec == noexcept_false_spec
> > || TREE_PURPOSE (spec) == error_mark_node
> > + || UNPARSED_NOEXCEPT_SPEC_P (spec)
> > || processing_template_decl);
> >
> > return false;
> > diff --git gcc/cp/parser.c gcc/cp/parser.c
> > index 12beadf5096..41197ab3486 100644
> > --- gcc/cp/parser.c
> > +++ gcc/cp/parser.c
> > @@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
> >
> > static cp_token_cache *cp_token_cache_new
> > (cp_token *, cp_token *);
> > +static tree cp_parser_noexcept_specification_opt
> > + (cp_parser *, bool, bool *, bool);
> > +static tree cp_parser_late_noexcept_specifier
> > + (cp_parser *, tree);
> > +static void noexcept_override_late_checks
> > + (tree, tree);
> >
> > static void cp_parser_initial_pragma
> > (cp_token *);
> > @@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
> > parser->unparsed_queues->last ().nsdmis
> > #define unparsed_classes \
> > parser->unparsed_queues->last ().classes
> > +#define unparsed_noexcepts \
> > + parser->unparsed_queues->last ().noexcepts
> >
> > static void
> > push_unparsed_function_queues (cp_parser *parser)
> > {
> > - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
> > + cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
> > + NULL };
> > vec_safe_push (parser->unparsed_queues, e);
> > }
> >
> > @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
> > /*asmspec=*/NULL_TREE,
> > attr_chainon (attributes, prefix_attributes));
> > if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> > - cp_parser_save_default_args (parser, decl);
> > + {
> > + cp_parser_save_default_args (parser, decl);
> > + /* Remember if there is a noexcept-specifier to post process. */
> > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > + vec_safe_push (unparsed_noexcepts, decl);
> > + }
> > cp_finalize_omp_declare_simd (parser, decl);
> > cp_finalize_oacc_routine (parser, decl, false);
> > }
> > @@ -23334,6 +23349,45 @@ cp_parser_class_name (cp_parser *parser,
> > return decl;
> > }
> >
> > +/* Make sure that any member-function parameters are in scope.
> > + For instance, a function's noexcept-specifier can use the function's
> > + parameters:
> > +
> > + struct S {
> > + void fn (int p) noexcept(noexcept(p));
> > + };
> > +
> > + so we need to make sure name lookup can find them. This is used
> > + when we delay parsing of the noexcept-specifier. */
> > +
> > +static void
> > +inject_parm_decls (tree decl)
> > +{
> > + begin_scope (sk_function_parms, decl);
> > + tree args = DECL_ARGUMENTS (decl);
> > + args = nreverse (args);
> > +
> > + tree next;
> > + for (tree parm = args; parm; parm = next)
> > + {
> > + next = DECL_CHAIN (parm);
> > + if (TREE_CODE (parm) == PARM_DECL)
> > + pushdecl (parm);
> > + }
> > + /* Get the decls in their original chain order and record in the
> > + function. This is all and only the PARM_DECLs that were
> > + pushed into scope by the loop above. */
> > + DECL_ARGUMENTS (decl) = get_local_decls ();
> > +}
> > +
> > +/* Undo the effects of inject_parm_decls. */
> > +
> > +static void
> > +pop_injected_parms (void)
> > +{
> > + pop_bindings_and_leave_scope ();
> > +}
> > +
> > /* Parse a class-specifier.
> >
> > class-specifier:
> > @@ -23644,6 +23698,66 @@ cp_parser_class_specifier_1 (cp_parser* parser)
> > vec_safe_truncate (unparsed_classes, 0);
> > after_nsdmi_defaulted_late_checks (type);
> >
> > + /* If there are noexcept-specifiers that have not yet been processed,
> > + take care of them now. */
> > + class_type = NULL_TREE;
> > + pushed_scope = NULL_TREE;
> > + FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
> > + {
> > + tree ctx = (DECL_FRIEND_P (decl) ? DECL_FRIEND_CONTEXT (decl)
> > + : DECL_CONTEXT (decl));
> > + if (class_type != ctx)
> > + {
> > + if (pushed_scope)
> > + pop_scope (pushed_scope);
> > + class_type = ctx;
> > + pushed_scope = push_scope (class_type);
> > + }
> > +
> > + /* Make sure that any template parameters are in scope. */
> > + maybe_begin_member_template_processing (decl);
> > +
> > + /* Make sure that any member-function parameters are in scope. */
> > + inject_parm_decls (decl);
> > +
> > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > + spec = TREE_PURPOSE (spec);
> > + tree old_decl = DEFARG_DECL (spec);
> > +
> > + /* 'this' is not allowed in static member functions. */
> > + unsigned char local_variables_forbidden_p
> > + = parser->local_variables_forbidden_p;
> > + if (DECL_THIS_STATIC (decl))
> > + parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
> > +
> > + /* Now we can parse the noexcept-specifier. */
> > + spec = cp_parser_late_noexcept_specifier (parser, spec);
> > +
> > + /* Restore the state of local_variables_forbidden_p. */
> > + parser->local_variables_forbidden_p = local_variables_forbidden_p;
> > + if (spec != error_mark_node)
> > + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
> > +
> > + /* If we've stashed an old declaration, it means we need to
> > + perform late redeclaration checking. */
> > + if (old_decl)
> > + check_redeclaration_exception_specification (decl, old_decl);
> > +
> > + /* The finish_struct call above performed various override checking,
> > + but it skipped unparsed noexcept-specifier operands. Now that we
> > + have resolved them, check again. */
> > + noexcept_override_late_checks (type, decl);
> > +
> > + /* Remove any member-function parameters from the symbol table. */
> > + pop_injected_parms ();
> > +
> > + /* Remove any template parameters from the symbol table. */
> > + maybe_end_member_template_processing ();
> > + }
> > + vec_safe_truncate (unparsed_noexcepts, 0);
> > + if (pushed_scope)
> > + pop_scope (pushed_scope);
> > +
> > /* Now parse the body of the functions. */
> > if (flag_openmp)
> > {
> > @@ -24817,6 +24931,12 @@ cp_parser_member_declaration (cp_parser* parser)
> > else
> > decl = finish_fully_implicit_template (parser, decl);
> > }
> > + if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> > + {
> > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > + vec_safe_push (unparsed_noexcepts, decl);
> > + }
> > }
> >
> > cp_finalize_omp_declare_simd (parser, decl);
> > @@ -25199,6 +25319,90 @@ cp_parser_base_specifier (cp_parser* parser)
> >
> > /* Exception handling [gram.exception] */
> >
> > +/* Save the tokens that make up the noexcept-specifier for a member-function.
> > + Returns a DEFAULT_ARG. */
> > +
> > +static tree
> > +cp_parser_save_noexcept (cp_parser *parser)
> > +{
> > + cp_token *first = parser->lexer->next_token;
> > + /* We want everything up to, including, the final ')'. */
> > + cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
> > + cp_token *last = parser->lexer->next_token;
> > +
> > + /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
> > + to carry the information we will need. */
> > + tree expr = make_node (DEFAULT_ARG);
> > + /* Save away the noexcept-specifier; we will process it when the
> > + class is complete. */
> > + DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
> > + DEFARG_DECL (expr) = NULL_TREE;
> > + DEFARG_NOEXCEPT_P (expr) = true;
> > + expr = build_tree_list (expr, NULL_TREE);
> > + return expr;
> > +}
> > +
> > +/* Used for late processing of noexcept-specifiers of member-functions.
> > + DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
> > + we saved for later; parse it now. */
> > +
> > +static tree
> > +cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
> > +{
> > + /* Make sure we've gotten something that hasn't been parsed yet. */
> > + gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
> > +
> > + push_unparsed_function_queues (parser);
> > +
> > + /* Push the saved tokens for the noexcept-specifier onto the parser's
> > + lexer stack. */
> > + cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
> > + cp_parser_push_lexer_for_tokens (parser, tokens);
> > +
> > + /* Parse the cached noexcept-specifier. */
> > + tree parsed_arg
> > + = cp_parser_noexcept_specification_opt (parser,
> > + /*require_constexpr=*/true,
> > + NULL,
> > + /*return_cond=*/false);
> > +
> > + /* Revert to the main lexer. */
> > + cp_parser_pop_lexer (parser);
> > +
> > + /* Restore the queue. */
> > + pop_unparsed_function_queues (parser);
> > +
> > + /* And we're done. */
> > + return parsed_arg;
> > +}
> > +
> > +/* Perform late checking of overriding function with respect to their
> > + noexcept-specifiers. TYPE is the class and FNDECL is the function
> > + that potentially overrides some virtual function with the same
> > + signature. */
> > +
> > +static void
> > +noexcept_override_late_checks (tree type, tree fndecl)
> > +{
> > + tree binfo = TYPE_BINFO (type);
> > + tree base_binfo;
> > +
> > + if (DECL_STATIC_FUNCTION_P (fndecl))
> > + return;
> > +
> > + for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
> > + {
> > + tree basetype = BINFO_TYPE (base_binfo);
> > +
> > + if (!TYPE_POLYMORPHIC_P (basetype))
> > + continue;
> > +
> > + tree fn = look_for_overrides_here (basetype, fndecl);
> > + if (fn)
> > + maybe_check_throw_specifier (fndecl, fn);
> > + }
> > +}
> > +
> > /* Parse an (optional) noexcept-specification.
> >
> > noexcept-specification:
> > @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > if (cp_parser_is_keyword (token, RID_NOEXCEPT))
> > {
> > tree expr;
> > +
> > + /* [class.mem]/6 says that a noexcept-specifer (within the
> > + member-specification of the class) is a complete-class context of
> > + a class. So, if the noexcept-specifier has the optional expression,
> > + just save the tokens, and reparse this after we're done with the
> > + class. */
> > + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
> > + && at_class_scope_p ()
> > + && TYPE_BEING_DEFINED (current_class_type)
> > + && !LAMBDA_TYPE_P (current_class_type))
> > + return cp_parser_save_noexcept (parser);
> > +
> > cp_lexer_consume_token (parser->lexer);
> >
> > if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
> > @@ -28427,7 +28643,12 @@ cp_parser_save_member_function_body (cp_parser* parser,
> > return error_mark_node;
> > }
> >
> > - /* Remember it, if there default args to post process. */
> > + /* Remember if there is a noexcept-specifier to post process. */
> > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
> > + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > + vec_safe_push (unparsed_noexcepts, fn);
> > +
> > + /* Remember it, if there are default args to post process. */
> > cp_parser_save_default_args (parser, fn);
> >
> > /* Save away the tokens that make up the body of the
> > diff --git gcc/cp/parser.h gcc/cp/parser.h
> > index c03a9d87af5..2890788f489 100644
> > --- gcc/cp/parser.h
> > +++ gcc/cp/parser.h
> > @@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
> > /* Nested classes go in this vector, so that we can do some final
> > processing after parsing any NSDMIs. */
> > vec<tree, va_gc> *classes;
> > +
> > + /* Functions with noexcept-specifiers that require post-processing. */
> > + vec<tree, va_gc> *noexcepts;
> > };
> >
> >
> > diff --git gcc/cp/pt.c gcc/cp/pt.c
> > index d6976e08690..c00d14fd954 100644
> > --- gcc/cp/pt.c
> > +++ gcc/cp/pt.c
> > @@ -25308,8 +25308,9 @@ dependent_type_p_r (tree type)
> > if (tree noex = TREE_PURPOSE (spec))
> > /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
> > affect overload resolution and treating it as dependent breaks
> > - things. */
> > + things. Same for an unparsed noexcept expression. */
> > if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
> > + && TREE_CODE (noex) != DEFAULT_ARG
> > && value_dependent_expression_p (noex))
> > return true;
> > return false;
> > diff --git gcc/cp/search.c gcc/cp/search.c
> > index 4c3fffda717..5a3a0cf2824 100644
> > --- gcc/cp/search.c
> > +++ gcc/cp/search.c
> > @@ -1863,6 +1863,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
> > NULL, &lfd);
> > }
> >
> > +/* Check throw specifier of OVERRIDER is at least as strict as
> > + the one of BASEFN. */
> > +
> > +bool
> > +maybe_check_throw_specifier (tree overrider, tree basefn)
> > +{
> > + maybe_instantiate_noexcept (basefn);
> > + maybe_instantiate_noexcept (overrider);
> > + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> > + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> > +
> > + if (DECL_INVALID_OVERRIDER_P (overrider))
> > + return true;
> > +
> > + /* Can't check this yet. Pretend this is fine and let
> > + noexcept_override_late_checks check this later. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> > + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> > + return true;
> > +
> > + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> > + {
> > + auto_diagnostic_group d;
> > + error ("looser exception specification on overriding virtual function "
> > + "%q+#F", overrider);
> > + inform (DECL_SOURCE_LOCATION (basefn),
> > + "overridden function is %q#F", basefn);
> > + DECL_INVALID_OVERRIDER_P (overrider) = 1;
> > + return false;
> > + }
> > + return true;
> > +}
> > +
> > /* Check that virtual overrider OVERRIDER is acceptable for base function
> > BASEFN. Issue diagnostic, and return zero, if unacceptable. */
> >
> > @@ -1873,7 +1906,6 @@ check_final_overrider (tree overrider, tree basefn)
> > tree base_type = TREE_TYPE (basefn);
> > tree over_return = fndecl_declared_return_type (overrider);
> > tree base_return = fndecl_declared_return_type (basefn);
> > - tree over_throw, base_throw;
> >
> > int fail = 0;
> >
> > @@ -1957,21 +1989,8 @@ check_final_overrider (tree overrider, tree basefn)
> > return 0;
> > }
> >
> > - /* Check throw specifier is at least as strict. */
> > - maybe_instantiate_noexcept (basefn);
> > - maybe_instantiate_noexcept (overrider);
> > - base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> > - over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> > -
> > - if (!comp_except_specs (base_throw, over_throw, ce_derived))
> > - {
> > - auto_diagnostic_group d;
> > - error ("looser throw specifier for %q+#F", overrider);
> > - inform (DECL_SOURCE_LOCATION (basefn),
> > - "overridden function is %q#F", basefn);
> > - DECL_INVALID_OVERRIDER_P (overrider) = 1;
> > - return 0;
> > - }
> > + if (!maybe_check_throw_specifier (overrider, basefn))
> > + return 0;
> >
> > /* Check for conflicting type attributes. But leave transaction_safe for
> > set_one_vmethod_tm_attributes. */
> > diff --git gcc/cp/tree.c gcc/cp/tree.c
> > index 718eed349c6..bc0080d6720 100644
> > --- gcc/cp/tree.c
> > +++ gcc/cp/tree.c
> > @@ -2550,6 +2550,7 @@ canonical_eh_spec (tree raises)
> > if (raises == NULL_TREE)
> > return raises;
> > else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
> > + || UNPARSED_NOEXCEPT_SPEC_P (raises)
> > || uses_template_parms (raises)
> > || uses_template_parms (TREE_PURPOSE (raises)))
> > /* Keep a dependent or deferred exception specification. */
> > @@ -3662,6 +3663,7 @@ cp_tree_equal (tree t1, tree t2)
> > case IDENTIFIER_NODE:
> > case SSA_NAME:
> > case USING_DECL:
> > + case DEFAULT_ARG:
> > return false;
> >
> > case BASELINK:
> > diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> > index df002a1664c..8cbc48fb44f 100644
> > --- gcc/cp/typeck2.c
> > +++ gcc/cp/typeck2.c
> > @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
> > if (list == error_mark_node || add == error_mark_node)
> > return error_mark_node;
> >
> > + /* We don't want to lose the unparsed operand lest we miss diagnostics. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> > + return list;
> > + else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> > + return add;
> > +
> > /* No exception-specifier or noexcept(false) are less strict than
> > anything else. Prefer the newer variant (LIST). */
> > if (!list || list == noexcept_false_spec)
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> > new file mode 100644
> > index 00000000000..43b38c2446f
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> > @@ -0,0 +1,147 @@
> > +// PR c++/86476 - noexcept-specifier is a complete-class context
> > +// { dg-do compile { target c++11 } }
> > +
> > +#define SA(X) static_assert(X, #X)
> > +
> > +struct S {
> > + void f1() noexcept(noexcept(i)) { }
> > + void f2() noexcept(noexcept(fn())) { }
> > + void f3() noexcept(noexcept(fnx())) { }
> > + void f4() noexcept(noexcept(i));
> > + void f5() noexcept(noexcept(fn()));
> > + void f6() noexcept(noexcept(fnx()));
> > +
> > + void f7() noexcept(1);
> > + void f8() noexcept(0);
> > + void f9() noexcept(b);
> > + void f10() noexcept(!b);
> > +
> > + int i;
> > + static constexpr auto b = true;
> > + void fny() noexcept(noexcept(fn()));
> > + void fn();
> > + void fnx() noexcept;
> > +};
> > +
> > +S s;
> > +SA(noexcept(s.f1()));
> > +SA(!noexcept(s.f2()));
> > +SA(noexcept(s.f3()));
> > +SA(noexcept(s.f4()));
> > +SA(!noexcept(s.f5()));
> > +SA(noexcept(s.f6()));
> > +SA(noexcept(s.f7()));
> > +SA(!noexcept(s.f8()));
> > +SA(noexcept(s.f9()));
> > +SA(!noexcept(s.f10()));
> > +
> > +struct S2 {
> > + struct V {
> > + void f1() noexcept(noexcept(fn()));
> > + void f2() noexcept(noexcept(fnx()));
> > + void f3() noexcept(noexcept(fn())) { }
> > + void f4() noexcept(noexcept(fnx())) { }
> > + void fn();
> > + void fnx() noexcept;
> > + } v;
> > + void fn();
> > + void fnx();
> > +};
> > +
> > +S2 s2;
> > +SA(!noexcept(s2.v.f1()));
> > +SA(noexcept(s2.v.f2()));
> > +SA(!noexcept(s2.v.f3()));
> > +SA(noexcept(s2.v.f4()));
> > +
> > +struct S3 {
> > + void f1() noexcept(noexcept(fn()));
> > + void f2() noexcept(noexcept(fnx()));
> > + void fn();
> > + void fnx() noexcept;
> > +};
> > +
> > +void
> > +S3::f1() noexcept(noexcept(fn()))
> > +{
> > +}
> > +
> > +void
> > +S3::f2() noexcept(noexcept(fnx()))
> > +{
> > +}
> > +
> > +struct S4 {
> > + int f1 (int p) noexcept(noexcept(p)) { return p; }
> > + int f2 (int p) noexcept(noexcept(p));
> > + int f3 (int p = 10) noexcept(noexcept(p));
> > + int f4 () noexcept(noexcept(S4{}));
> > +};
> > +
> > +S4 s4;
> > +SA(noexcept(s4.f1(1)));
> > +SA(noexcept(s4.f2(1)));
> > +SA(noexcept(s4.f3()));
> > +SA(noexcept(s4.f4()));
> > +
> > +template<typename T>
> > +struct S5 {
> > + void f1() noexcept(noexcept(i)) { }
> > + void f2() noexcept(noexcept(fn())) { }
> > + void f3() noexcept(noexcept(fnx())) { }
> > + void f4() noexcept(noexcept(i));
> > + void f5() noexcept(noexcept(fn()));
> > + void f6() noexcept(noexcept(fnx()));
> > +
> > + int i;
> > + void fny() noexcept(noexcept(fn()));
> > + void fn();
> > + void fnx() noexcept;
> > +};
> > +
> > +S5<int> s5;
> > +SA(noexcept(s5.f1()));
> > +SA(!noexcept(s5.f2()));
> > +SA(noexcept(s5.f3()));
> > +SA(noexcept(s5.f4()));
> > +SA(!noexcept(s5.f5()));
> > +SA(noexcept(s5.f6()));
> > +
> > +template<typename T>
> > +struct S6 {
> > + void f1() noexcept(noexcept(x));
> > + T x;
> > +};
> > +
> > +struct S7 {
> > + template<typename U>
> > + void f1 () noexcept(noexcept(U(1))) { }
> > +
> > + template<int N>
> > + void f2() noexcept(noexcept(N));
> > +
> > + template <typename _Up>
> > + void f3(_Up __p) noexcept(noexcept(__p));
> > +};
> > +
> > +void glob();
> > +void globx() noexcept;
> > +struct S8 {
> > + void f1 () noexcept(noexcept(glob()));
> > + void f2 () noexcept(noexcept(globx()));
> > +};
> > +
> > +S8 s8;
> > +SA(!noexcept(s8.f1()));
> > +SA(noexcept(s8.f2()));
> > +
> > +struct W {
> > + constexpr operator bool();
> > +};
> > +
> > +template<typename T>
> > +struct S9 {
> > + S9() noexcept(noexcept(w)) { }
> > + S9 &operator=(S9 &&) noexcept(T::X);
> > + W w;
> > +};
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> > new file mode 100644
> > index 00000000000..b3859de9ebc
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> > @@ -0,0 +1,26 @@
> > +// PR c++/86476 - noexcept-specifier is a complete-class context
> > +// { dg-do compile { target c++11 } }
> > +
> > +struct S {
> > + void f1() noexcept(noexcept(fn()));
> > + void f2() noexcept(noexcept(fnx()));
> > + void fn();
> > + void fnx() noexcept;
> > +};
> > +
> > +void
> > +S::f1() noexcept // { dg-error "different exception specifier" }
> > +{
> > +}
> > +
> > +void
> > +S::f2() // { dg-error "different exception specifier" }
> > +{
> > +}
> > +
> > +struct S2 {
> > + void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
> > + void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
> > + void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
> > + void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
> > +};
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> > new file mode 100644
> > index 00000000000..12c6d364099
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> > @@ -0,0 +1,9 @@
> > +// PR c++/86476 - noexcept-specifier is a complete-class context
> > +// { dg-do compile { target c++11 } }
> > +
> > +template <typename _Alloc> class A {
> > + typedef _Alloc _Alloc_traits;
> > + A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> > + A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> > + void m_fn2(A<char>) {}
> > +};
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> > new file mode 100644
> > index 00000000000..a81032f28e9
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> > @@ -0,0 +1,14 @@
> > +// PR c++/86476 - noexcept-specifier is a complete-class context
> > +// { dg-do compile { target c++11 } }
> > +
> > +void fn1(void());
> > +template <typename> class A {
> > + void _M_local_data();
> > + A() noexcept(_M_local_data);
> > +};
> > +
> > +class B {
> > + void _S_initialize();
> > + static void _S_initialize_once();
> > +};
> > +void B::_S_initialize() { fn1(_S_initialize_once); }
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> > new file mode 100644
> > index 00000000000..39df4a6571e
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> > @@ -0,0 +1,23 @@
> > +// PR c++/86476 - noexcept-specifier is a complete-class context
> > +// { dg-do compile { target c++11 } }
> > +
> > +struct A
> > +{
> > + virtual void f();
> > + virtual void g() noexcept;
> > + virtual void h() noexcept(false);
> > +};
> > +
> > +struct B : A
> > +{
> > + void f() noexcept(true);
> > + void g() noexcept(true);
> > + void h() noexcept(true);
> > +};
> > +
> > +struct D : A
> > +{
> > + void f() noexcept(false);
> > + void g() noexcept(false); // { dg-error "looser exception specification" }
> > + void h() noexcept(false);
> > +};
> > diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> > new file mode 100644
> > index 00000000000..da7490d651c
> > --- /dev/null
> > +++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> > @@ -0,0 +1,28 @@
> > +// PR c++/86476 - noexcept-specifier is a complete-class context
> > +// { dg-do compile { target c++11 } }
> > +
> > +void f() noexcept(false);
> > +void g() noexcept(true);
> > +void h() noexcept;
> > +
> > +struct B {
> > + friend void f() noexcept(false);
> > + friend void g() noexcept(false); // { dg-error "different exception specifier" }
> > + friend void h() noexcept(false); // { dg-error "different exception specifier" }
> > +};
> > +
> > +struct C {
> > + friend void f() noexcept(true); // { dg-error "different exception specifier" }
> > + friend void g() noexcept(true); // { dg-error "different exception specifier" }
> > + friend void h() noexcept(true); // { dg-error "different exception specifier" }
> > +};
> > +
> > +void o() noexcept(false);
> > +void p() noexcept(true);
> > +void q() noexcept;
> > +
> > +struct D {
> > + friend void o() noexcept(true); // { dg-error "different exception specifier" }
> > + friend void p() noexcept(true);
> > + friend void q() noexcept(true);
> > +};
> > diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
> > index 0ba6145ef0c..6bccc704d49 100644
> > --- gcc/testsuite/g++.dg/eh/shadow1.C
> > +++ gcc/testsuite/g++.dg/eh/shadow1.C
> > @@ -18,7 +18,7 @@ struct D : private B
> > // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
> > struct E : public D
> > {
> > - virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
> > + virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
> > }; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
> > // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
> > B* foo (D *);
>
> Marek
Marek
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-05-10 19:21 ` Marek Polacek
2019-05-17 14:35 ` Marek Polacek
@ 2019-05-28 15:48 ` Jason Merrill
2019-06-04 1:02 ` Marek Polacek
1 sibling, 1 reply; 14+ messages in thread
From: Jason Merrill @ 2019-05-28 15:48 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 5/10/19 3:21 PM, Marek Polacek wrote:
> Coming back to this. I didn't think this was suitable for GCC 9.
>
> On Mon, Jan 07, 2019 at 10:44:37AM -0500, Jason Merrill wrote:
>> On 12/19/18 3:27 PM, Marek Polacek wrote:
>>> Prompted by Jon's observation in 52869, I noticed that we don't treat
>>> a noexcept-specifier as a complete-class context of a class ([class.mem]/6).
>>> As with member function bodies, default arguments, and NSDMIs, names used in
>>> a noexcept-specifier of a member-function can be declared later in the class
>>> body, so we need to wait and parse them at the end of the class.
>>> For that, I've made use of DEFAULT_ARG (now best to be renamed to UNPARSED_ARG).
>>
>> Or DEFERRED_PARSE, yes.
>
> I didn't change the name but I'm happy to do it as a follow up.
>
>>> + /* We can't compare unparsed noexcept-specifiers. Save the old decl
>>> + and check this again after we've parsed the noexcept-specifiers
>>> + for real. */
>>> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
>>> + {
>>> + vec_safe_push (DEFARG_INSTANTIATIONS (TREE_PURPOSE (new_exceptions)),
>>> + copy_decl (old_decl));
>>> + return;
>>> + }
>>
>> Why copy_decl?
>
> This is so that we don't lose the diagnostic in noexcept46.C. If I don't use
> copy_decl then the tree is shared and subsequent changes to it make us not
> detect discrepancies like noexcept(false) vs. noexcept(true) on the same decl.
OK, fair enough.
> @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
> /*asmspec=*/NULL_TREE,
> attr_chainon (attributes, prefix_attributes));
> if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> - cp_parser_save_default_args (parser, decl);
> + {
> + cp_parser_save_default_args (parser, decl);
> + /* Remember if there is a noexcept-specifier to post process. */
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, decl);
Can we handle this in cp_parser_save_default_args rather than all its
callers?
> +/* Make sure that any member-function parameters are in scope.
> + For instance, a function's noexcept-specifier can use the function's
> + parameters:
> +
> + struct S {
> + void fn (int p) noexcept(noexcept(p));
> + };
> +
> + so we need to make sure name lookup can find them. This is used
> + when we delay parsing of the noexcept-specifier. */
> +
> +static void
> +inject_parm_decls (tree decl)
> +{
> + begin_scope (sk_function_parms, decl);
> + tree args = DECL_ARGUMENTS (decl);
> + args = nreverse (args);
> +
> + tree next;
> + for (tree parm = args; parm; parm = next)
> + {
> + next = DECL_CHAIN (parm);
> + if (TREE_CODE (parm) == PARM_DECL)
> + pushdecl (parm);
> + }
> + /* Get the decls in their original chain order and record in the
> + function. This is all and only the PARM_DECLs that were
> + pushed into scope by the loop above. */
> + DECL_ARGUMENTS (decl) = get_local_decls ();
> +}
Can we share this code with store_parm_decls instead of having two copies?
> @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> + /* [class.mem]/6 says that a noexcept-specifer (within the
> + member-specification of the class) is a complete-class context of
> + a class. So, if the noexcept-specifier has the optional expression,
> + just save the tokens, and reparse this after we're done with the
> + class. */
> + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
> + && at_class_scope_p ()
> + && TYPE_BEING_DEFINED (current_class_type)
> + && !LAMBDA_TYPE_P (current_class_type))
> + return cp_parser_save_noexcept (parser);
We might optimize the noexcept(<literal>) case, which should be pretty
common.
> +maybe_check_throw_specifier (tree overrider, tree basefn)
maybe_check_overriding_exception_spec
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index df002a1664c..8cbc48fb44f 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
> if (list == error_mark_node || add == error_mark_node)
> return error_mark_node;
>
> + /* We don't want to lose the unparsed operand lest we miss diagnostics. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> + return list;
> + else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> + return add;
Here you're throwing away the other side, which seems wrong.
Jason
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-05-28 15:48 ` Jason Merrill
@ 2019-06-04 1:02 ` Marek Polacek
2019-06-10 12:28 ` Marek Polacek
2019-06-12 3:46 ` Jason Merrill
0 siblings, 2 replies; 14+ messages in thread
From: Marek Polacek @ 2019-06-04 1:02 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Tue, May 28, 2019 at 11:46:53AM -0400, Jason Merrill wrote:
> > @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
> > /*asmspec=*/NULL_TREE,
> > attr_chainon (attributes, prefix_attributes));
> > if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> > - cp_parser_save_default_args (parser, decl);
> > + {
> > + cp_parser_save_default_args (parser, decl);
> > + /* Remember if there is a noexcept-specifier to post process. */
> > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > + vec_safe_push (unparsed_noexcepts, decl);
>
> Can we handle this in cp_parser_save_default_args rather than all its
> callers?
Yes, done.
> > +/* Make sure that any member-function parameters are in scope.
> > + For instance, a function's noexcept-specifier can use the function's
> > + parameters:
> > +
> > + struct S {
> > + void fn (int p) noexcept(noexcept(p));
> > + };
> > +
> > + so we need to make sure name lookup can find them. This is used
> > + when we delay parsing of the noexcept-specifier. */
> > +
> > +static void
> > +inject_parm_decls (tree decl)
> > +{
> > + begin_scope (sk_function_parms, decl);
> > + tree args = DECL_ARGUMENTS (decl);
> > + args = nreverse (args);
> > +
> > + tree next;
> > + for (tree parm = args; parm; parm = next)
> > + {
> > + next = DECL_CHAIN (parm);
> > + if (TREE_CODE (parm) == PARM_DECL)
> > + pushdecl (parm);
> > + }
> > + /* Get the decls in their original chain order and record in the
> > + function. This is all and only the PARM_DECLs that were
> > + pushed into scope by the loop above. */
> > + DECL_ARGUMENTS (decl) = get_local_decls ();
> > +}
>
> Can we share this code with store_parm_decls instead of having two copies?
Makes sense, the nreverse logic is tricky enough not to duplicate it. I named
the new function do_push_parm_decls.
> > @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > + /* [class.mem]/6 says that a noexcept-specifer (within the
> > + member-specification of the class) is a complete-class context of
> > + a class. So, if the noexcept-specifier has the optional expression,
> > + just save the tokens, and reparse this after we're done with the
> > + class. */
> > + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
> > + && at_class_scope_p ()
> > + && TYPE_BEING_DEFINED (current_class_type)
> > + && !LAMBDA_TYPE_P (current_class_type))
> > + return cp_parser_save_noexcept (parser);
>
> We might optimize the noexcept(<literal>) case, which should be pretty
> common.
Yeah, it's worth it. Added for numbers/false/true.
> > +maybe_check_throw_specifier (tree overrider, tree basefn)
>
> maybe_check_overriding_exception_spec
Changed.
> > diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> > index df002a1664c..8cbc48fb44f 100644
> > --- gcc/cp/typeck2.c
> > +++ gcc/cp/typeck2.c
> > @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
> > if (list == error_mark_node || add == error_mark_node)
> > return error_mark_node;
> > + /* We don't want to lose the unparsed operand lest we miss diagnostics. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> > + return list;
> > + else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> > + return add;
>
> Here you're throwing away the other side, which seems wrong.
I sort of ended up going down a rathole, but then I realized we don't need to
delay parsing of noexcept-specifiers of member friend function declarations,
because they aren't members of the class. This was a huge relief because
member friend function declarations can be redeclared, so we'd have to make
sure to check if their noexcept-specifiers match. But member function decls
can't be redeclared. I updated the comment to better reflect why what I'm
doing there is correct, along with an assert.
noexcept47.C tests various cases with friend declarations.
Thanks,
Bootstrapped/regtested on x86_64-linux, ok for trunk?
2019-06-03 Marek Polacek <polacek@redhat.com>
PR c++/86476 - noexcept-specifier is a complete-class context.
PR c++/52869
* cp-tree.def (DEFAULT_ARG): Update commentary.
* cp-tree.h (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P):
New macros.
(tree_default_arg): Add a tree field, make the last two fields into a
union. Add GTY markers.
(check_redeclaration_exception_specification, do_push_parm_decls,
maybe_check_overriding_exception_spec): Declare.
* decl.c (check_redeclaration_exception_specification): No longer
static. Handle UNPARSED_NOEXCEPT_SPEC_P.
(do_push_parm_decls): New function, broken out of...
(store_parm_decls): ...here.
* except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
* parser.c (cp_parser_noexcept_specification_opt,
cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
Forward-declare.
(unparsed_noexcepts): New macro.
(push_unparsed_function_queues): Update initializer.
(cp_parser_direct_declarator): Pass FRIEND_P to
cp_parser_exception_specification_opt.
(inject_parm_decls): New.
(pop_injected_parms): New.
(cp_parser_class_specifier_1): Implement delayed parsing of
noexcept-specifiers.
(cp_parser_save_noexcept): New.
(cp_parser_late_noexcept_specifier): New.
(noexcept_override_late_checks): New.
(cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call
cp_parser_save_noexcept instead of the normal processing if needed.
(cp_parser_exception_specification_opt): Add FRIEND_P parameter and
pass it to cp_parser_noexcept_specification_opt.
(cp_parser_save_member_function_body): Fix comment.
(cp_parser_save_default_args): Maybe save the noexcept-specifier to
post process.
(cp_parser_transaction): Update call to
cp_parser_noexcept_specification_opt.
(cp_parser_transaction_expression): Likewise.
* parser.h (cp_unparsed_functions_entry): Add new field to carry
a noexcept-specifier.
* pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
* search.c (maybe_check_overriding_exception_spec): New function, broken
out of...
(check_final_overrider): ...here. Call
maybe_check_overriding_exception_spec.
* tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
(cp_tree_equal): Handle DEFAULT_ARG.
* typeck2.c (merge_exception_specifiers): If an unparsed noexcept
expression has been passed, return it instead of merging it.
* g++.dg/cpp0x/noexcept41.C: New test.
* g++.dg/cpp0x/noexcept42.C: New test.
* g++.dg/cpp0x/noexcept43.C: New test.
* g++.dg/cpp0x/noexcept44.C: New test.
* g++.dg/cpp0x/noexcept45.C: New test.
* g++.dg/cpp0x/noexcept46.C: New test.
* g++.dg/cpp0x/noexcept47.C: New test.
* g++.dg/cpp0x/noexcept48.C: New test.
* g++.dg/cpp0x/noexcept49.C: New test.
* g++.dg/eh/shadow1.C: Adjust dg-error.
diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 03c105b5c4c..33eb5d25efe 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
/* An un-parsed default argument. Holds a vector of input tokens and
a vector of places where the argument was instantiated before
- parsing had occurred. */
+ parsing had occurred. This is also used for delayed NSDMIs and
+ noexcept-specifier parsing. For a noexcept-specifier, we use a tree
+ holding a function declaration used for late checking. */
DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
/* An uninstantiated/unevaluated noexcept-specification. For the
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 4d79c43c5af..3f1d8a853db 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1187,12 +1187,20 @@ enum cp_identifier_kind {
#define DEFARG_TOKENS(NODE) \
(((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens)
#define DEFARG_INSTANTIATIONS(NODE) \
- (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
+ (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations)
+#define DEFARG_DECL(NODE) \
+ (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl)
+#define DEFARG_NOEXCEPT_P(NODE) \
+ (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p)
struct GTY (()) tree_default_arg {
- struct tree_common common;
+ struct tree_base base;
struct cp_token_cache *tokens;
- vec<tree, va_gc> *instantiations;
+ BOOL_BITFIELD noexcept_p : 1;
+ union {
+ vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
+ tree GTY((tag ("1"))) decl;
+ } GTY((desc ("%1.noexcept_p"))) u;
};
@@ -1206,6 +1214,9 @@ struct GTY (()) tree_default_arg {
#define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
(DEFERRED_NOEXCEPT_SPEC_P (NODE) \
&& DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
+#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
+ ((NODE) && (TREE_PURPOSE (NODE)) \
+ && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
struct GTY (()) tree_deferred_noexcept {
struct tree_base base;
@@ -6467,6 +6478,9 @@ extern bool check_array_designated_initializer (constructor_elt *,
unsigned HOST_WIDE_INT);
extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
extern tree build_explicit_specifier (tree, tsubst_flags_t);
+extern void check_redeclaration_exception_specification
+ (tree, tree);
+extern void do_push_parm_decls (tree, tree, tree *);
/* in decl2.c */
extern void record_mangling (tree, bool);
@@ -6932,6 +6947,7 @@ extern tree copied_binfo (tree, tree);
extern tree original_binfo (tree, tree);
extern int shared_member_p (tree);
extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
+extern bool maybe_check_overriding_exception_spec (tree, tree);
/* The representation of a deferred access check. */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index bdf397e5ecb..e8fdc08bd4a 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
function templates. If their exception specifications do not
match, issue a diagnostic. */
-static void
+void
check_redeclaration_exception_specification (tree new_decl,
tree old_decl)
{
@@ -1151,6 +1151,18 @@ check_redeclaration_exception_specification (tree new_decl,
&& UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
return;
+ /* We can't compare unparsed noexcept-specifiers. Save the decl
+ and check this again after we've parsed the noexcept-specifiers
+ for real. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
+ {
+ DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
+ return;
+ }
+ else
+ /* Member functions can't be redeclared. */
+ gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (old_exceptions));
+
if (!type_dependent_expression_p (old_decl))
{
maybe_instantiate_noexcept (new_decl);
@@ -15706,6 +15718,39 @@ use_eh_spec_block (tree fn)
&& !DECL_DEFAULTED_FN (fn));
}
+/* Helper function to push ARGS into the current lexical scope. DECL
+ is the function declaration. NONPARMS is used to handle enum
+ constants. */
+
+void
+do_push_parm_decls (tree decl, tree args, tree *nonparms)
+{
+ /* If we're doing semantic analysis, then we'll call pushdecl
+ for each of these. We must do them in reverse order so that
+ they end in the correct forward order. */
+ args = nreverse (args);
+
+ tree next;
+ for (tree parm = args; parm; parm = next)
+ {
+ next = DECL_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ pushdecl (parm);
+ else if (nonparms)
+ {
+ /* If we find an enum constant or a type tag, put it aside for
+ the moment. */
+ TREE_CHAIN (parm) = NULL_TREE;
+ *nonparms = chainon (*nonparms, parm);
+ }
+ }
+
+ /* Get the decls in their original chain order and record in the
+ function. This is all and only the PARM_DECLs that were
+ pushed into scope by the loop above. */
+ DECL_ARGUMENTS (decl) = get_local_decls ();
+}
+
/* Store the parameter declarations into the current function declaration.
This is called after parsing the parameter declarations, before
digesting the body of the function.
@@ -15716,7 +15761,6 @@ static void
store_parm_decls (tree current_function_parms)
{
tree fndecl = current_function_decl;
- tree parm;
/* This is a chain of any other decls that came in among the parm
declarations. If a parm is declared with enum {foo, bar} x;
@@ -15731,35 +15775,12 @@ store_parm_decls (tree current_function_parms)
and complain if any redundant old-style parm decls were written. */
tree specparms = current_function_parms;
- tree next;
/* Must clear this because it might contain TYPE_DECLs declared
at class level. */
current_binding_level->names = NULL;
- /* If we're doing semantic analysis, then we'll call pushdecl
- for each of these. We must do them in reverse order so that
- they end in the correct forward order. */
- specparms = nreverse (specparms);
-
- for (parm = specparms; parm; parm = next)
- {
- next = DECL_CHAIN (parm);
- if (TREE_CODE (parm) == PARM_DECL)
- pushdecl (parm);
- else
- {
- /* If we find an enum constant or a type tag,
- put it aside for the moment. */
- TREE_CHAIN (parm) = NULL_TREE;
- nonparms = chainon (nonparms, parm);
- }
- }
-
- /* Get the decls in their original chain order and record in the
- function. This is all and only the PARM_DECLs that were
- pushed into scope by the loop above. */
- DECL_ARGUMENTS (fndecl) = get_local_decls ();
+ do_push_parm_decls (fndecl, specparms, &nonparms);
}
else
DECL_ARGUMENTS (fndecl) = NULL_TREE;
diff --git gcc/cp/except.c gcc/cp/except.c
index 892d5201da9..8d7b1e9bac7 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
|| TREE_VALUE (spec)
|| spec == noexcept_false_spec
|| TREE_PURPOSE (spec) == error_mark_node
+ || UNPARSED_NOEXCEPT_SPEC_P (spec)
|| processing_template_decl);
return false;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 1de35da83ec..ea4adbcdb14 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
static cp_token_cache *cp_token_cache_new
(cp_token *, cp_token *);
+static tree cp_parser_noexcept_specification_opt
+ (cp_parser *, bool, bool *, bool, bool);
+static tree cp_parser_late_noexcept_specifier
+ (cp_parser *, tree);
+static void noexcept_override_late_checks
+ (tree, tree);
static void cp_parser_initial_pragma
(cp_token *);
@@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_classes \
parser->unparsed_queues->last ().classes
+#define unparsed_noexcepts \
+ parser->unparsed_queues->last ().noexcepts
static void
push_unparsed_function_queues (cp_parser *parser)
{
- cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
+ cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
+ NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -2361,7 +2370,7 @@ static tree cp_parser_exception_declaration
static tree cp_parser_throw_expression
(cp_parser *);
static tree cp_parser_exception_specification_opt
- (cp_parser *);
+ (cp_parser *, bool = false);
static tree cp_parser_type_id_list
(cp_parser *);
@@ -20804,7 +20813,7 @@ cp_parser_direct_declarator (cp_parser* parser,
tree tx_qual = cp_parser_tx_qualifier_opt (parser);
/* And the exception-specification. */
exception_specification
- = cp_parser_exception_specification_opt (parser);
+ = cp_parser_exception_specification_opt (parser, friend_p);
attrs = cp_parser_std_attribute_spec_seq (parser);
@@ -23298,6 +23307,34 @@ cp_parser_class_name (cp_parser *parser,
return decl;
}
+/* Make sure that any member-function parameters are in scope.
+ For instance, a function's noexcept-specifier can use the function's
+ parameters:
+
+ struct S {
+ void fn (int p) noexcept(noexcept(p));
+ };
+
+ so we need to make sure name lookup can find them. This is used
+ when we delay parsing of the noexcept-specifier. */
+
+static void
+inject_parm_decls (tree decl)
+{
+ begin_scope (sk_function_parms, decl);
+ tree args = DECL_ARGUMENTS (decl);
+
+ do_push_parm_decls (decl, args, /*nonparms=*/NULL);
+}
+
+/* Undo the effects of inject_parm_decls. */
+
+static void
+pop_injected_parms (void)
+{
+ pop_bindings_and_leave_scope ();
+}
+
/* Parse a class-specifier.
class-specifier:
@@ -23608,6 +23645,67 @@ cp_parser_class_specifier_1 (cp_parser* parser)
vec_safe_truncate (unparsed_classes, 0);
after_nsdmi_defaulted_late_checks (type);
+ /* If there are noexcept-specifiers that have not yet been processed,
+ take care of them now. */
+ class_type = NULL_TREE;
+ pushed_scope = NULL_TREE;
+ FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+ {
+ tree ctx = DECL_CONTEXT (decl);
+ if (class_type != ctx)
+ {
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+ class_type = ctx;
+ pushed_scope = push_scope (class_type);
+ }
+
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ spec = TREE_PURPOSE (spec);
+
+ /* Make sure that any template parameters are in scope. */
+ maybe_begin_member_template_processing (decl);
+
+ /* Make sure that any member-function parameters are in scope. */
+ inject_parm_decls (decl);
+
+ tree old_decl = DEFARG_DECL (spec);
+
+ /* 'this' is not allowed in static member functions. */
+ unsigned char local_variables_forbidden_p
+ = parser->local_variables_forbidden_p;
+ if (DECL_THIS_STATIC (decl))
+ parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+
+ /* Now we can parse the noexcept-specifier. */
+ spec = cp_parser_late_noexcept_specifier (parser, spec);
+
+ if (spec != error_mark_node)
+ TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
+
+ /* If we've stashed an old declaration, it means we need to
+ perform late redeclaration checking. */
+ if (old_decl)
+ check_redeclaration_exception_specification (decl, old_decl);
+
+ /* Restore the state of local_variables_forbidden_p. */
+ parser->local_variables_forbidden_p = local_variables_forbidden_p;
+
+ /* The finish_struct call above performed various override checking,
+ but it skipped unparsed noexcept-specifier operands. Now that we
+ have resolved them, check again. */
+ noexcept_override_late_checks (type, decl);
+
+ /* Remove any member-function parameters from the symbol table. */
+ pop_injected_parms ();
+
+ /* Remove any template parameters from the symbol table. */
+ maybe_end_member_template_processing ();
+ }
+ vec_safe_truncate (unparsed_noexcepts, 0);
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+
/* Now parse the body of the functions. */
if (flag_openmp)
{
@@ -25163,6 +25261,91 @@ cp_parser_base_specifier (cp_parser* parser)
/* Exception handling [gram.exception] */
+/* Save the tokens that make up the noexcept-specifier for a member-function.
+ Returns a DEFAULT_ARG. */
+
+static tree
+cp_parser_save_noexcept (cp_parser *parser)
+{
+ cp_token *first = parser->lexer->next_token;
+ /* We want everything up to, including, the final ')'. */
+ cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
+ cp_token *last = parser->lexer->next_token;
+
+ /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
+ to carry the information we will need. */
+ tree expr = make_node (DEFAULT_ARG);
+ /* Save away the noexcept-specifier; we will process it when the
+ class is complete. */
+ DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
+ DEFARG_DECL (expr) = NULL_TREE;
+ DEFARG_NOEXCEPT_P (expr) = true;
+ expr = build_tree_list (expr, NULL_TREE);
+ return expr;
+}
+
+/* Used for late processing of noexcept-specifiers of member-functions.
+ DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
+ we saved for later; parse it now. */
+
+static tree
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+{
+ /* Make sure we've gotten something that hasn't been parsed yet. */
+ gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
+
+ push_unparsed_function_queues (parser);
+
+ /* Push the saved tokens for the noexcept-specifier onto the parser's
+ lexer stack. */
+ cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
+ cp_parser_push_lexer_for_tokens (parser, tokens);
+
+ /* Parse the cached noexcept-specifier. */
+ tree parsed_arg
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/false,
+ /*friend_p=*/false);
+
+ /* Revert to the main lexer. */
+ cp_parser_pop_lexer (parser);
+
+ /* Restore the queue. */
+ pop_unparsed_function_queues (parser);
+
+ /* And we're done. */
+ return parsed_arg;
+}
+
+/* Perform late checking of overriding function with respect to their
+ noexcept-specifiers. TYPE is the class and FNDECL is the function
+ that potentially overrides some virtual function with the same
+ signature. */
+
+static void
+noexcept_override_late_checks (tree type, tree fndecl)
+{
+ tree binfo = TYPE_BINFO (type);
+ tree base_binfo;
+
+ if (DECL_STATIC_FUNCTION_P (fndecl))
+ return;
+
+ for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+ {
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ if (!TYPE_POLYMORPHIC_P (basetype))
+ continue;
+
+ tree fn = look_for_overrides_here (basetype, fndecl);
+ if (fn)
+ maybe_check_overriding_exception_spec (fndecl, fn);
+ }
+}
+
/* Parse an (optional) noexcept-specification.
noexcept-specification:
@@ -25173,13 +25356,15 @@ cp_parser_base_specifier (cp_parser* parser)
expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
there are no parentheses. CONSUMED_EXPR will be set accordingly.
Otherwise, returns a noexcept specification unless RETURN_COND is true,
- in which case a boolean condition is returned instead. */
+ in which case a boolean condition is returned instead. If FRIEND_P is true,
+ the function with this noexcept-specification had the `friend' specifier. */
static tree
cp_parser_noexcept_specification_opt (cp_parser* parser,
bool require_constexpr,
bool* consumed_expr,
- bool return_cond)
+ bool return_cond,
+ bool friend_p)
{
cp_token *token;
const char *saved_message;
@@ -25191,6 +25376,26 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
if (cp_parser_is_keyword (token, RID_NOEXCEPT))
{
tree expr;
+
+ /* [class.mem]/6 says that a noexcept-specifer (within the
+ member-specification of the class) is a complete-class context of
+ a class. So, if the noexcept-specifier has the optional expression,
+ just save the tokens, and reparse this after we're done with the
+ class. */
+ if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+ /* No need to delay parsing for a number literal or true/false. */
+ && !cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
+ && !(cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)
+ && (cp_lexer_nth_token_is_keyword (parser->lexer, 3, RID_FALSE)
+ || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
+ RID_TRUE)))
+ && at_class_scope_p ()
+ /* Don't delay parsing for friend member functions. */
+ && !friend_p
+ && TYPE_BEING_DEFINED (current_class_type)
+ && !LAMBDA_TYPE_P (current_class_type))
+ return cp_parser_save_noexcept (parser);
+
cp_lexer_consume_token (parser->lexer);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
@@ -25261,10 +25466,11 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
throw ( type-id-list [opt] )
Returns a TREE_LIST representing the exception-specification. The
- TREE_VALUE of each node is a type. */
+ TREE_VALUE of each node is a type. If FRIEND_P is true, the function
+ with this noexcept-specification had the `friend' specifier. */
static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
{
cp_token *token;
tree type_id_list;
@@ -25274,8 +25480,12 @@ cp_parser_exception_specification_opt (cp_parser* parser)
token = cp_lexer_peek_token (parser->lexer);
/* Is it a noexcept-specification? */
- type_id_list = cp_parser_noexcept_specification_opt (parser, true, NULL,
- false);
+ type_id_list
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/false,
+ friend_p);
if (type_id_list != NULL_TREE)
return type_id_list;
@@ -28403,7 +28613,7 @@ cp_parser_save_member_function_body (cp_parser* parser,
return error_mark_node;
}
- /* Remember it, if there default args to post process. */
+ /* Remember it, if there are default args to post process. */
cp_parser_save_default_args (parser, fn);
/* Save away the tokens that make up the body of the
@@ -28696,6 +28906,11 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
vec_safe_push (unparsed_funs_with_default_args, entry);
break;
}
+
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
}
/* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
@@ -40506,7 +40721,11 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
noex = NULL_TREE;
}
else
- noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
+ noex = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/true,
+ /*friend_p=*/false);
/* Keep track if we're in the lexical scope of an outer transaction. */
new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -40566,8 +40785,11 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
parser->in_transaction = this_in;
/* Parse a noexcept specification. */
- noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr,
- true);
+ noex = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/false,
+ &noex_expr,
+ /*return_cond=*/true,
+ /*friend_p=*/false);
if (!noex || !noex_expr
|| cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git gcc/cp/parser.h gcc/cp/parser.h
index c03a9d87af5..2890788f489 100644
--- gcc/cp/parser.h
+++ gcc/cp/parser.h
@@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Nested classes go in this vector, so that we can do some final
processing after parsing any NSDMIs. */
vec<tree, va_gc> *classes;
+
+ /* Functions with noexcept-specifiers that require post-processing. */
+ vec<tree, va_gc> *noexcepts;
};
diff --git gcc/cp/pt.c gcc/cp/pt.c
index cfbd9fd4c88..0324bd404e9 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -25302,8 +25302,9 @@ dependent_type_p_r (tree type)
if (tree noex = TREE_PURPOSE (spec))
/* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
affect overload resolution and treating it as dependent breaks
- things. */
+ things. Same for an unparsed noexcept expression. */
if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
+ && TREE_CODE (noex) != DEFAULT_ARG
&& value_dependent_expression_p (noex))
return true;
return false;
diff --git gcc/cp/search.c gcc/cp/search.c
index dac08d44d76..372c4424747 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -1860,6 +1860,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
NULL, &lfd);
}
+/* Check throw specifier of OVERRIDER is at least as strict as
+ the one of BASEFN. */
+
+bool
+maybe_check_overriding_exception_spec (tree overrider, tree basefn)
+{
+ maybe_instantiate_noexcept (basefn);
+ maybe_instantiate_noexcept (overrider);
+ tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+ tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
+ if (DECL_INVALID_OVERRIDER_P (overrider))
+ return true;
+
+ /* Can't check this yet. Pretend this is fine and let
+ noexcept_override_late_checks check this later. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
+ || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
+ return true;
+
+ if (!comp_except_specs (base_throw, over_throw, ce_derived))
+ {
+ auto_diagnostic_group d;
+ error ("looser exception specification on overriding virtual function "
+ "%q+#F", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %q#F", basefn);
+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
+ return false;
+ }
+ return true;
+}
+
/* Check that virtual overrider OVERRIDER is acceptable for base function
BASEFN. Issue diagnostic, and return zero, if unacceptable. */
@@ -1870,7 +1903,6 @@ check_final_overrider (tree overrider, tree basefn)
tree base_type = TREE_TYPE (basefn);
tree over_return = fndecl_declared_return_type (overrider);
tree base_return = fndecl_declared_return_type (basefn);
- tree over_throw, base_throw;
int fail = 0;
@@ -1954,21 +1986,8 @@ check_final_overrider (tree overrider, tree basefn)
return 0;
}
- /* Check throw specifier is at least as strict. */
- maybe_instantiate_noexcept (basefn);
- maybe_instantiate_noexcept (overrider);
- base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
- over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
-
- if (!comp_except_specs (base_throw, over_throw, ce_derived))
- {
- auto_diagnostic_group d;
- error ("looser throw specifier for %q+#F", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#F", basefn);
- DECL_INVALID_OVERRIDER_P (overrider) = 1;
- return 0;
- }
+ if (!maybe_check_overriding_exception_spec (overrider, basefn))
+ return 0;
/* Check for conflicting type attributes. But leave transaction_safe for
set_one_vmethod_tm_attributes. */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index cd021b7f594..81c53b23ebf 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2546,6 +2546,7 @@ canonical_eh_spec (tree raises)
if (raises == NULL_TREE)
return raises;
else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+ || UNPARSED_NOEXCEPT_SPEC_P (raises)
|| uses_template_parms (raises)
|| uses_template_parms (TREE_PURPOSE (raises)))
/* Keep a dependent or deferred exception specification. */
@@ -3656,6 +3657,7 @@ cp_tree_equal (tree t1, tree t2)
case IDENTIFIER_NODE:
case SSA_NAME:
case USING_DECL:
+ case DEFAULT_ARG:
return false;
case BASELINK:
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index e9f759d4213..eec5550bf2d 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -2391,6 +2391,15 @@ merge_exception_specifiers (tree list, tree add)
if (list == error_mark_node || add == error_mark_node)
return error_mark_node;
+ /* We don't want to lose the unparsed operand lest we miss diagnostics.
+ We can use the newer variant, because the old one will be saved in
+ DEFARG_DECL's noexcept-specifier. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (list))
+ return list;
+ else
+ /* Member functions can't be redeclared. */
+ gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (add));
+
/* No exception-specifier or noexcept(false) are less strict than
anything else. Prefer the newer variant (LIST). */
if (!list || list == noexcept_false_spec)
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C gcc/testsuite/g++.dg/cpp0x/noexcept41.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept41.C
@@ -0,0 +1,147 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ void f7() noexcept(1);
+ void f8() noexcept(0);
+ void f9() noexcept(b);
+ void f10() noexcept(!b);
+
+ int i;
+ static constexpr auto b = true;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S s;
+SA(noexcept(s.f1()));
+SA(!noexcept(s.f2()));
+SA(noexcept(s.f3()));
+SA(noexcept(s.f4()));
+SA(!noexcept(s.f5()));
+SA(noexcept(s.f6()));
+SA(noexcept(s.f7()));
+SA(!noexcept(s.f8()));
+SA(noexcept(s.f9()));
+SA(!noexcept(s.f10()));
+
+struct S2 {
+ struct V {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void f3() noexcept(noexcept(fn())) { }
+ void f4() noexcept(noexcept(fnx())) { }
+ void fn();
+ void fnx() noexcept;
+ } v;
+ void fn();
+ void fnx();
+};
+
+S2 s2;
+SA(!noexcept(s2.v.f1()));
+SA(noexcept(s2.v.f2()));
+SA(!noexcept(s2.v.f3()));
+SA(noexcept(s2.v.f4()));
+
+struct S3 {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S3::f1() noexcept(noexcept(fn()))
+{
+}
+
+void
+S3::f2() noexcept(noexcept(fnx()))
+{
+}
+
+struct S4 {
+ int f1 (int p) noexcept(noexcept(p)) { return p; }
+ int f2 (int p) noexcept(noexcept(p));
+ int f3 (int p = 10) noexcept(noexcept(p));
+ int f4 () noexcept(noexcept(S4{}));
+};
+
+S4 s4;
+SA(noexcept(s4.f1(1)));
+SA(noexcept(s4.f2(1)));
+SA(noexcept(s4.f3()));
+SA(noexcept(s4.f4()));
+
+template<typename T>
+struct S5 {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ int i;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S5<int> s5;
+SA(noexcept(s5.f1()));
+SA(!noexcept(s5.f2()));
+SA(noexcept(s5.f3()));
+SA(noexcept(s5.f4()));
+SA(!noexcept(s5.f5()));
+SA(noexcept(s5.f6()));
+
+template<typename T>
+struct S6 {
+ void f1() noexcept(noexcept(x));
+ T x;
+};
+
+struct S7 {
+ template<typename U>
+ void f1 () noexcept(noexcept(U(1))) { }
+
+ template<int N>
+ void f2() noexcept(noexcept(N));
+
+ template <typename _Up>
+ void f3(_Up __p) noexcept(noexcept(__p));
+};
+
+void glob();
+void globx() noexcept;
+struct S8 {
+ void f1 () noexcept(noexcept(glob()));
+ void f2 () noexcept(noexcept(globx()));
+};
+
+S8 s8;
+SA(!noexcept(s8.f1()));
+SA(noexcept(s8.f2()));
+
+struct W {
+ constexpr operator bool();
+};
+
+template<typename T>
+struct S9 {
+ S9() noexcept(noexcept(w)) { }
+ S9 &operator=(S9 &&) noexcept(T::X);
+ W w;
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
@@ -0,0 +1,26 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct S {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S::f1() noexcept // { dg-error "different exception specifier" }
+{
+}
+
+void
+S::f2() // { dg-error "different exception specifier" }
+{
+}
+
+struct S2 {
+ void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
+ void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
+ void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
+ void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
@@ -0,0 +1,9 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+template <typename _Alloc> class A {
+ typedef _Alloc _Alloc_traits;
+ A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ void m_fn2(A<char>) {}
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
@@ -0,0 +1,14 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void fn1(void());
+template <typename> class A {
+ void _M_local_data();
+ A() noexcept(_M_local_data);
+};
+
+class B {
+ void _S_initialize();
+ static void _S_initialize_once();
+};
+void B::_S_initialize() { fn1(_S_initialize_once); }
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
new file mode 100644
index 00000000000..39df4a6571e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
@@ -0,0 +1,23 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+ virtual void f();
+ virtual void g() noexcept;
+ virtual void h() noexcept(false);
+};
+
+struct B : A
+{
+ void f() noexcept(true);
+ void g() noexcept(true);
+ void h() noexcept(true);
+};
+
+struct D : A
+{
+ void f() noexcept(false);
+ void g() noexcept(false); // { dg-error "looser exception specification" }
+ void h() noexcept(false);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
new file mode 100644
index 00000000000..da7490d651c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
@@ -0,0 +1,28 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void f() noexcept(false);
+void g() noexcept(true);
+void h() noexcept;
+
+struct B {
+ friend void f() noexcept(false);
+ friend void g() noexcept(false); // { dg-error "different exception specifier" }
+ friend void h() noexcept(false); // { dg-error "different exception specifier" }
+};
+
+struct C {
+ friend void f() noexcept(true); // { dg-error "different exception specifier" }
+ friend void g() noexcept(true); // { dg-error "different exception specifier" }
+ friend void h() noexcept(true); // { dg-error "different exception specifier" }
+};
+
+void o() noexcept(false);
+void p() noexcept(true);
+void q() noexcept;
+
+struct D {
+ friend void o() noexcept(true); // { dg-error "different exception specifier" }
+ friend void p() noexcept(true);
+ friend void q() noexcept(true);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644
index 00000000000..0848e68f9b1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
@@ -0,0 +1,83 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int fn1 ();
+int fn2 () noexcept;
+int fn3 () noexcept;
+
+void g() noexcept(noexcept (fn2()));
+
+struct S1 {
+ friend void g1() noexcept(noexcept(fn2()));
+ friend void g1() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S2 {
+ friend void g2() noexcept(noexcept(fn1()));
+ friend void g2() noexcept(noexcept(fn1()));
+ friend void g2() noexcept(noexcept(fn1()));
+};
+
+struct S3 {
+ friend void g3() noexcept(noexcept(fn1()));
+ friend void g3() noexcept(noexcept(fn3())); // { dg-error "different exception specifier" }
+};
+
+struct S4 {
+ friend void g4() noexcept(noexcept(fn2()));
+ friend void g4() noexcept(noexcept(fn3()));
+};
+
+struct S5 {
+ friend void g() noexcept(noexcept(fn3()));
+};
+
+struct S6 {
+ friend void g() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S7 {
+ friend void gg() noexcept(noexcept(fn3()));
+};
+
+void gg() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+
+struct S8 {
+ friend void g8();
+ friend void g8() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S9 {
+ friend void g9();
+ friend void g9() noexcept(noexcept(fn1()));
+};
+
+struct S10 {
+ friend void g10() noexcept(noexcept(fn1()));
+ friend void g10();
+};
+
+struct S11 {
+ friend void g11() noexcept(noexcept(fn2()));
+ friend void g11(); // { dg-error "different exception specifier" }
+};
+
+struct S12 {
+ friend void g12() noexcept(false);
+ friend void g12() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S13 {
+ friend void g13() noexcept(false);
+ friend void g13() noexcept(noexcept(fn1()));
+};
+
+struct S14 {
+ friend void g14() noexcept(noexcept(fn1()));
+ friend void g14() noexcept(false);
+};
+
+struct S15 {
+ friend void g15() noexcept(noexcept(fn2()));
+ friend void g15() noexcept(false); // { dg-error "different exception specifier" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644
index 00000000000..134212c3613
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
@@ -0,0 +1,11 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int g;
+
+struct S {
+ int b;
+ friend void fn1(int n) noexcept(noexcept(n));
+ friend void fn2() noexcept(noexcept(g));
+ friend void fn3() noexcept(noexcept(b));
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644
index 00000000000..6da7ff3361f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
@@ -0,0 +1,12 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ static void f1() noexcept(b);
+ static constexpr auto b = true;
+};
+
+S s;
+SA(noexcept(s.f1()));
diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
index 0ba6145ef0c..6bccc704d49 100644
--- gcc/testsuite/g++.dg/eh/shadow1.C
+++ gcc/testsuite/g++.dg/eh/shadow1.C
@@ -18,7 +18,7 @@ struct D : private B
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
struct E : public D
{
- virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
+ virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
}; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
B* foo (D *);
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-06-04 1:02 ` Marek Polacek
@ 2019-06-10 12:28 ` Marek Polacek
2019-06-12 3:46 ` Jason Merrill
1 sibling, 0 replies; 14+ messages in thread
From: Marek Polacek @ 2019-06-10 12:28 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
Ping.
On Mon, Jun 03, 2019 at 09:01:37PM -0400, Marek Polacek wrote:
> On Tue, May 28, 2019 at 11:46:53AM -0400, Jason Merrill wrote:
> > > @@ -20515,7 +20524,13 @@ cp_parser_init_declarator (cp_parser* parser,
> > > /*asmspec=*/NULL_TREE,
> > > attr_chainon (attributes, prefix_attributes));
> > > if (decl && TREE_CODE (decl) == FUNCTION_DECL)
> > > - cp_parser_save_default_args (parser, decl);
> > > + {
> > > + cp_parser_save_default_args (parser, decl);
> > > + /* Remember if there is a noexcept-specifier to post process. */
> > > + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> > > + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> > > + vec_safe_push (unparsed_noexcepts, decl);
> >
> > Can we handle this in cp_parser_save_default_args rather than all its
> > callers?
>
> Yes, done.
>
> > > +/* Make sure that any member-function parameters are in scope.
> > > + For instance, a function's noexcept-specifier can use the function's
> > > + parameters:
> > > +
> > > + struct S {
> > > + void fn (int p) noexcept(noexcept(p));
> > > + };
> > > +
> > > + so we need to make sure name lookup can find them. This is used
> > > + when we delay parsing of the noexcept-specifier. */
> > > +
> > > +static void
> > > +inject_parm_decls (tree decl)
> > > +{
> > > + begin_scope (sk_function_parms, decl);
> > > + tree args = DECL_ARGUMENTS (decl);
> > > + args = nreverse (args);
> > > +
> > > + tree next;
> > > + for (tree parm = args; parm; parm = next)
> > > + {
> > > + next = DECL_CHAIN (parm);
> > > + if (TREE_CODE (parm) == PARM_DECL)
> > > + pushdecl (parm);
> > > + }
> > > + /* Get the decls in their original chain order and record in the
> > > + function. This is all and only the PARM_DECLs that were
> > > + pushed into scope by the loop above. */
> > > + DECL_ARGUMENTS (decl) = get_local_decls ();
> > > +}
> >
> > Can we share this code with store_parm_decls instead of having two copies?
>
> Makes sense, the nreverse logic is tricky enough not to duplicate it. I named
> the new function do_push_parm_decls.
>
> > > @@ -25227,6 +25431,18 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > > + /* [class.mem]/6 says that a noexcept-specifer (within the
> > > + member-specification of the class) is a complete-class context of
> > > + a class. So, if the noexcept-specifier has the optional expression,
> > > + just save the tokens, and reparse this after we're done with the
> > > + class. */
> > > + if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_PAREN
> > > + && at_class_scope_p ()
> > > + && TYPE_BEING_DEFINED (current_class_type)
> > > + && !LAMBDA_TYPE_P (current_class_type))
> > > + return cp_parser_save_noexcept (parser);
> >
> > We might optimize the noexcept(<literal>) case, which should be pretty
> > common.
>
> Yeah, it's worth it. Added for numbers/false/true.
>
> > > +maybe_check_throw_specifier (tree overrider, tree basefn)
> >
> > maybe_check_overriding_exception_spec
>
> Changed.
>
> > > diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> > > index df002a1664c..8cbc48fb44f 100644
> > > --- gcc/cp/typeck2.c
> > > +++ gcc/cp/typeck2.c
> > > @@ -2393,6 +2393,12 @@ merge_exception_specifiers (tree list, tree add)
> > > if (list == error_mark_node || add == error_mark_node)
> > > return error_mark_node;
> > > + /* We don't want to lose the unparsed operand lest we miss diagnostics. */
> > > + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> > > + return list;
> > > + else if (UNPARSED_NOEXCEPT_SPEC_P (add))
> > > + return add;
> >
> > Here you're throwing away the other side, which seems wrong.
>
> I sort of ended up going down a rathole, but then I realized we don't need to
> delay parsing of noexcept-specifiers of member friend function declarations,
> because they aren't members of the class. This was a huge relief because
> member friend function declarations can be redeclared, so we'd have to make
> sure to check if their noexcept-specifiers match. But member function decls
> can't be redeclared. I updated the comment to better reflect why what I'm
> doing there is correct, along with an assert.
>
> noexcept47.C tests various cases with friend declarations.
>
> Thanks,
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2019-06-03 Marek Polacek <polacek@redhat.com>
>
> PR c++/86476 - noexcept-specifier is a complete-class context.
> PR c++/52869
> * cp-tree.def (DEFAULT_ARG): Update commentary.
> * cp-tree.h (DEFARG_DECL, DEFARG_NOEXCEPT_P, UNPARSED_NOEXCEPT_SPEC_P):
> New macros.
> (tree_default_arg): Add a tree field, make the last two fields into a
> union. Add GTY markers.
> (check_redeclaration_exception_specification, do_push_parm_decls,
> maybe_check_overriding_exception_spec): Declare.
> * decl.c (check_redeclaration_exception_specification): No longer
> static. Handle UNPARSED_NOEXCEPT_SPEC_P.
> (do_push_parm_decls): New function, broken out of...
> (store_parm_decls): ...here.
> * except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
> * parser.c (cp_parser_noexcept_specification_opt,
> cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
> Forward-declare.
> (unparsed_noexcepts): New macro.
> (push_unparsed_function_queues): Update initializer.
> (cp_parser_direct_declarator): Pass FRIEND_P to
> cp_parser_exception_specification_opt.
> (inject_parm_decls): New.
> (pop_injected_parms): New.
> (cp_parser_class_specifier_1): Implement delayed parsing of
> noexcept-specifiers.
> (cp_parser_save_noexcept): New.
> (cp_parser_late_noexcept_specifier): New.
> (noexcept_override_late_checks): New.
> (cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call
> cp_parser_save_noexcept instead of the normal processing if needed.
> (cp_parser_exception_specification_opt): Add FRIEND_P parameter and
> pass it to cp_parser_noexcept_specification_opt.
> (cp_parser_save_member_function_body): Fix comment.
> (cp_parser_save_default_args): Maybe save the noexcept-specifier to
> post process.
> (cp_parser_transaction): Update call to
> cp_parser_noexcept_specification_opt.
> (cp_parser_transaction_expression): Likewise.
> * parser.h (cp_unparsed_functions_entry): Add new field to carry
> a noexcept-specifier.
> * pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
> * search.c (maybe_check_overriding_exception_spec): New function, broken
> out of...
> (check_final_overrider): ...here. Call
> maybe_check_overriding_exception_spec.
> * tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
> (cp_tree_equal): Handle DEFAULT_ARG.
> * typeck2.c (merge_exception_specifiers): If an unparsed noexcept
> expression has been passed, return it instead of merging it.
>
> * g++.dg/cpp0x/noexcept41.C: New test.
> * g++.dg/cpp0x/noexcept42.C: New test.
> * g++.dg/cpp0x/noexcept43.C: New test.
> * g++.dg/cpp0x/noexcept44.C: New test.
> * g++.dg/cpp0x/noexcept45.C: New test.
> * g++.dg/cpp0x/noexcept46.C: New test.
> * g++.dg/cpp0x/noexcept47.C: New test.
> * g++.dg/cpp0x/noexcept48.C: New test.
> * g++.dg/cpp0x/noexcept49.C: New test.
> * g++.dg/eh/shadow1.C: Adjust dg-error.
>
> diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> index 03c105b5c4c..33eb5d25efe 100644
> --- gcc/cp/cp-tree.def
> +++ gcc/cp/cp-tree.def
> @@ -209,7 +209,9 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
>
> /* An un-parsed default argument. Holds a vector of input tokens and
> a vector of places where the argument was instantiated before
> - parsing had occurred. */
> + parsing had occurred. This is also used for delayed NSDMIs and
> + noexcept-specifier parsing. For a noexcept-specifier, we use a tree
> + holding a function declaration used for late checking. */
> DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
>
> /* An uninstantiated/unevaluated noexcept-specification. For the
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index 4d79c43c5af..3f1d8a853db 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -1187,12 +1187,20 @@ enum cp_identifier_kind {
> #define DEFARG_TOKENS(NODE) \
> (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->tokens)
> #define DEFARG_INSTANTIATIONS(NODE) \
> - (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
> + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.instantiations)
> +#define DEFARG_DECL(NODE) \
> + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->u.decl)
> +#define DEFARG_NOEXCEPT_P(NODE) \
> + (((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->noexcept_p)
>
> struct GTY (()) tree_default_arg {
> - struct tree_common common;
> + struct tree_base base;
> struct cp_token_cache *tokens;
> - vec<tree, va_gc> *instantiations;
> + BOOL_BITFIELD noexcept_p : 1;
> + union {
> + vec<tree, va_gc>* GTY((tag ("0"))) instantiations;
> + tree GTY((tag ("1"))) decl;
> + } GTY((desc ("%1.noexcept_p"))) u;
> };
>
>
> @@ -1206,6 +1214,9 @@ struct GTY (()) tree_default_arg {
> #define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
> (DEFERRED_NOEXCEPT_SPEC_P (NODE) \
> && DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
> +#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
> + ((NODE) && (TREE_PURPOSE (NODE)) \
> + && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
>
> struct GTY (()) tree_deferred_noexcept {
> struct tree_base base;
> @@ -6467,6 +6478,9 @@ extern bool check_array_designated_initializer (constructor_elt *,
> unsigned HOST_WIDE_INT);
> extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
> extern tree build_explicit_specifier (tree, tsubst_flags_t);
> +extern void check_redeclaration_exception_specification
> + (tree, tree);
> +extern void do_push_parm_decls (tree, tree, tree *);
>
> /* in decl2.c */
> extern void record_mangling (tree, bool);
> @@ -6932,6 +6947,7 @@ extern tree copied_binfo (tree, tree);
> extern tree original_binfo (tree, tree);
> extern int shared_member_p (tree);
> extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
> +extern bool maybe_check_overriding_exception_spec (tree, tree);
>
> /* The representation of a deferred access check. */
>
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index bdf397e5ecb..e8fdc08bd4a 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -1139,7 +1139,7 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
> function templates. If their exception specifications do not
> match, issue a diagnostic. */
>
> -static void
> +void
> check_redeclaration_exception_specification (tree new_decl,
> tree old_decl)
> {
> @@ -1151,6 +1151,18 @@ check_redeclaration_exception_specification (tree new_decl,
> && UNEVALUATED_NOEXCEPT_SPEC_P (old_exceptions))
> return;
>
> + /* We can't compare unparsed noexcept-specifiers. Save the decl
> + and check this again after we've parsed the noexcept-specifiers
> + for real. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> + {
> + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> + return;
> + }
> + else
> + /* Member functions can't be redeclared. */
> + gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (old_exceptions));
> +
> if (!type_dependent_expression_p (old_decl))
> {
> maybe_instantiate_noexcept (new_decl);
> @@ -15706,6 +15718,39 @@ use_eh_spec_block (tree fn)
> && !DECL_DEFAULTED_FN (fn));
> }
>
> +/* Helper function to push ARGS into the current lexical scope. DECL
> + is the function declaration. NONPARMS is used to handle enum
> + constants. */
> +
> +void
> +do_push_parm_decls (tree decl, tree args, tree *nonparms)
> +{
> + /* If we're doing semantic analysis, then we'll call pushdecl
> + for each of these. We must do them in reverse order so that
> + they end in the correct forward order. */
> + args = nreverse (args);
> +
> + tree next;
> + for (tree parm = args; parm; parm = next)
> + {
> + next = DECL_CHAIN (parm);
> + if (TREE_CODE (parm) == PARM_DECL)
> + pushdecl (parm);
> + else if (nonparms)
> + {
> + /* If we find an enum constant or a type tag, put it aside for
> + the moment. */
> + TREE_CHAIN (parm) = NULL_TREE;
> + *nonparms = chainon (*nonparms, parm);
> + }
> + }
> +
> + /* Get the decls in their original chain order and record in the
> + function. This is all and only the PARM_DECLs that were
> + pushed into scope by the loop above. */
> + DECL_ARGUMENTS (decl) = get_local_decls ();
> +}
> +
> /* Store the parameter declarations into the current function declaration.
> This is called after parsing the parameter declarations, before
> digesting the body of the function.
> @@ -15716,7 +15761,6 @@ static void
> store_parm_decls (tree current_function_parms)
> {
> tree fndecl = current_function_decl;
> - tree parm;
>
> /* This is a chain of any other decls that came in among the parm
> declarations. If a parm is declared with enum {foo, bar} x;
> @@ -15731,35 +15775,12 @@ store_parm_decls (tree current_function_parms)
> and complain if any redundant old-style parm decls were written. */
>
> tree specparms = current_function_parms;
> - tree next;
>
> /* Must clear this because it might contain TYPE_DECLs declared
> at class level. */
> current_binding_level->names = NULL;
>
> - /* If we're doing semantic analysis, then we'll call pushdecl
> - for each of these. We must do them in reverse order so that
> - they end in the correct forward order. */
> - specparms = nreverse (specparms);
> -
> - for (parm = specparms; parm; parm = next)
> - {
> - next = DECL_CHAIN (parm);
> - if (TREE_CODE (parm) == PARM_DECL)
> - pushdecl (parm);
> - else
> - {
> - /* If we find an enum constant or a type tag,
> - put it aside for the moment. */
> - TREE_CHAIN (parm) = NULL_TREE;
> - nonparms = chainon (nonparms, parm);
> - }
> - }
> -
> - /* Get the decls in their original chain order and record in the
> - function. This is all and only the PARM_DECLs that were
> - pushed into scope by the loop above. */
> - DECL_ARGUMENTS (fndecl) = get_local_decls ();
> + do_push_parm_decls (fndecl, specparms, &nonparms);
> }
> else
> DECL_ARGUMENTS (fndecl) = NULL_TREE;
> diff --git gcc/cp/except.c gcc/cp/except.c
> index 892d5201da9..8d7b1e9bac7 100644
> --- gcc/cp/except.c
> +++ gcc/cp/except.c
> @@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
> || TREE_VALUE (spec)
> || spec == noexcept_false_spec
> || TREE_PURPOSE (spec) == error_mark_node
> + || UNPARSED_NOEXCEPT_SPEC_P (spec)
> || processing_template_decl);
>
> return false;
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index 1de35da83ec..ea4adbcdb14 100644
> --- gcc/cp/parser.c
> +++ gcc/cp/parser.c
> @@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
>
> static cp_token_cache *cp_token_cache_new
> (cp_token *, cp_token *);
> +static tree cp_parser_noexcept_specification_opt
> + (cp_parser *, bool, bool *, bool, bool);
> +static tree cp_parser_late_noexcept_specifier
> + (cp_parser *, tree);
> +static void noexcept_override_late_checks
> + (tree, tree);
>
> static void cp_parser_initial_pragma
> (cp_token *);
> @@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
> parser->unparsed_queues->last ().nsdmis
> #define unparsed_classes \
> parser->unparsed_queues->last ().classes
> +#define unparsed_noexcepts \
> + parser->unparsed_queues->last ().noexcepts
>
> static void
> push_unparsed_function_queues (cp_parser *parser)
> {
> - cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
> + cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
> + NULL };
> vec_safe_push (parser->unparsed_queues, e);
> }
>
> @@ -2361,7 +2370,7 @@ static tree cp_parser_exception_declaration
> static tree cp_parser_throw_expression
> (cp_parser *);
> static tree cp_parser_exception_specification_opt
> - (cp_parser *);
> + (cp_parser *, bool = false);
> static tree cp_parser_type_id_list
> (cp_parser *);
>
> @@ -20804,7 +20813,7 @@ cp_parser_direct_declarator (cp_parser* parser,
> tree tx_qual = cp_parser_tx_qualifier_opt (parser);
> /* And the exception-specification. */
> exception_specification
> - = cp_parser_exception_specification_opt (parser);
> + = cp_parser_exception_specification_opt (parser, friend_p);
>
> attrs = cp_parser_std_attribute_spec_seq (parser);
>
> @@ -23298,6 +23307,34 @@ cp_parser_class_name (cp_parser *parser,
> return decl;
> }
>
> +/* Make sure that any member-function parameters are in scope.
> + For instance, a function's noexcept-specifier can use the function's
> + parameters:
> +
> + struct S {
> + void fn (int p) noexcept(noexcept(p));
> + };
> +
> + so we need to make sure name lookup can find them. This is used
> + when we delay parsing of the noexcept-specifier. */
> +
> +static void
> +inject_parm_decls (tree decl)
> +{
> + begin_scope (sk_function_parms, decl);
> + tree args = DECL_ARGUMENTS (decl);
> +
> + do_push_parm_decls (decl, args, /*nonparms=*/NULL);
> +}
> +
> +/* Undo the effects of inject_parm_decls. */
> +
> +static void
> +pop_injected_parms (void)
> +{
> + pop_bindings_and_leave_scope ();
> +}
> +
> /* Parse a class-specifier.
>
> class-specifier:
> @@ -23608,6 +23645,67 @@ cp_parser_class_specifier_1 (cp_parser* parser)
> vec_safe_truncate (unparsed_classes, 0);
> after_nsdmi_defaulted_late_checks (type);
>
> + /* If there are noexcept-specifiers that have not yet been processed,
> + take care of them now. */
> + class_type = NULL_TREE;
> + pushed_scope = NULL_TREE;
> + FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
> + {
> + tree ctx = DECL_CONTEXT (decl);
> + if (class_type != ctx)
> + {
> + if (pushed_scope)
> + pop_scope (pushed_scope);
> + class_type = ctx;
> + pushed_scope = push_scope (class_type);
> + }
> +
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + spec = TREE_PURPOSE (spec);
> +
> + /* Make sure that any template parameters are in scope. */
> + maybe_begin_member_template_processing (decl);
> +
> + /* Make sure that any member-function parameters are in scope. */
> + inject_parm_decls (decl);
> +
> + tree old_decl = DEFARG_DECL (spec);
> +
> + /* 'this' is not allowed in static member functions. */
> + unsigned char local_variables_forbidden_p
> + = parser->local_variables_forbidden_p;
> + if (DECL_THIS_STATIC (decl))
> + parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
> +
> + /* Now we can parse the noexcept-specifier. */
> + spec = cp_parser_late_noexcept_specifier (parser, spec);
> +
> + if (spec != error_mark_node)
> + TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
> +
> + /* If we've stashed an old declaration, it means we need to
> + perform late redeclaration checking. */
> + if (old_decl)
> + check_redeclaration_exception_specification (decl, old_decl);
> +
> + /* Restore the state of local_variables_forbidden_p. */
> + parser->local_variables_forbidden_p = local_variables_forbidden_p;
> +
> + /* The finish_struct call above performed various override checking,
> + but it skipped unparsed noexcept-specifier operands. Now that we
> + have resolved them, check again. */
> + noexcept_override_late_checks (type, decl);
> +
> + /* Remove any member-function parameters from the symbol table. */
> + pop_injected_parms ();
> +
> + /* Remove any template parameters from the symbol table. */
> + maybe_end_member_template_processing ();
> + }
> + vec_safe_truncate (unparsed_noexcepts, 0);
> + if (pushed_scope)
> + pop_scope (pushed_scope);
> +
> /* Now parse the body of the functions. */
> if (flag_openmp)
> {
> @@ -25163,6 +25261,91 @@ cp_parser_base_specifier (cp_parser* parser)
>
> /* Exception handling [gram.exception] */
>
> +/* Save the tokens that make up the noexcept-specifier for a member-function.
> + Returns a DEFAULT_ARG. */
> +
> +static tree
> +cp_parser_save_noexcept (cp_parser *parser)
> +{
> + cp_token *first = parser->lexer->next_token;
> + /* We want everything up to, including, the final ')'. */
> + cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
> + cp_token *last = parser->lexer->next_token;
> +
> + /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
> + to carry the information we will need. */
> + tree expr = make_node (DEFAULT_ARG);
> + /* Save away the noexcept-specifier; we will process it when the
> + class is complete. */
> + DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
> + DEFARG_DECL (expr) = NULL_TREE;
> + DEFARG_NOEXCEPT_P (expr) = true;
> + expr = build_tree_list (expr, NULL_TREE);
> + return expr;
> +}
> +
> +/* Used for late processing of noexcept-specifiers of member-functions.
> + DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
> + we saved for later; parse it now. */
> +
> +static tree
> +cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
> +{
> + /* Make sure we've gotten something that hasn't been parsed yet. */
> + gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
> +
> + push_unparsed_function_queues (parser);
> +
> + /* Push the saved tokens for the noexcept-specifier onto the parser's
> + lexer stack. */
> + cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
> + cp_parser_push_lexer_for_tokens (parser, tokens);
> +
> + /* Parse the cached noexcept-specifier. */
> + tree parsed_arg
> + = cp_parser_noexcept_specification_opt (parser,
> + /*require_constexpr=*/true,
> + /*consumed_expr=*/NULL,
> + /*return_cond=*/false,
> + /*friend_p=*/false);
> +
> + /* Revert to the main lexer. */
> + cp_parser_pop_lexer (parser);
> +
> + /* Restore the queue. */
> + pop_unparsed_function_queues (parser);
> +
> + /* And we're done. */
> + return parsed_arg;
> +}
> +
> +/* Perform late checking of overriding function with respect to their
> + noexcept-specifiers. TYPE is the class and FNDECL is the function
> + that potentially overrides some virtual function with the same
> + signature. */
> +
> +static void
> +noexcept_override_late_checks (tree type, tree fndecl)
> +{
> + tree binfo = TYPE_BINFO (type);
> + tree base_binfo;
> +
> + if (DECL_STATIC_FUNCTION_P (fndecl))
> + return;
> +
> + for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
> + {
> + tree basetype = BINFO_TYPE (base_binfo);
> +
> + if (!TYPE_POLYMORPHIC_P (basetype))
> + continue;
> +
> + tree fn = look_for_overrides_here (basetype, fndecl);
> + if (fn)
> + maybe_check_overriding_exception_spec (fndecl, fn);
> + }
> +}
> +
> /* Parse an (optional) noexcept-specification.
>
> noexcept-specification:
> @@ -25173,13 +25356,15 @@ cp_parser_base_specifier (cp_parser* parser)
> expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
> there are no parentheses. CONSUMED_EXPR will be set accordingly.
> Otherwise, returns a noexcept specification unless RETURN_COND is true,
> - in which case a boolean condition is returned instead. */
> + in which case a boolean condition is returned instead. If FRIEND_P is true,
> + the function with this noexcept-specification had the `friend' specifier. */
>
> static tree
> cp_parser_noexcept_specification_opt (cp_parser* parser,
> bool require_constexpr,
> bool* consumed_expr,
> - bool return_cond)
> + bool return_cond,
> + bool friend_p)
> {
> cp_token *token;
> const char *saved_message;
> @@ -25191,6 +25376,26 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> if (cp_parser_is_keyword (token, RID_NOEXCEPT))
> {
> tree expr;
> +
> + /* [class.mem]/6 says that a noexcept-specifer (within the
> + member-specification of the class) is a complete-class context of
> + a class. So, if the noexcept-specifier has the optional expression,
> + just save the tokens, and reparse this after we're done with the
> + class. */
> + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
> + /* No need to delay parsing for a number literal or true/false. */
> + && !cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
> + && !(cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)
> + && (cp_lexer_nth_token_is_keyword (parser->lexer, 3, RID_FALSE)
> + || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
> + RID_TRUE)))
> + && at_class_scope_p ()
> + /* Don't delay parsing for friend member functions. */
> + && !friend_p
> + && TYPE_BEING_DEFINED (current_class_type)
> + && !LAMBDA_TYPE_P (current_class_type))
> + return cp_parser_save_noexcept (parser);
> +
> cp_lexer_consume_token (parser->lexer);
>
> if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
> @@ -25261,10 +25466,11 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> throw ( type-id-list [opt] )
>
> Returns a TREE_LIST representing the exception-specification. The
> - TREE_VALUE of each node is a type. */
> + TREE_VALUE of each node is a type. If FRIEND_P is true, the function
> + with this noexcept-specification had the `friend' specifier. */
>
> static tree
> -cp_parser_exception_specification_opt (cp_parser* parser)
> +cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
> {
> cp_token *token;
> tree type_id_list;
> @@ -25274,8 +25480,12 @@ cp_parser_exception_specification_opt (cp_parser* parser)
> token = cp_lexer_peek_token (parser->lexer);
>
> /* Is it a noexcept-specification? */
> - type_id_list = cp_parser_noexcept_specification_opt (parser, true, NULL,
> - false);
> + type_id_list
> + = cp_parser_noexcept_specification_opt (parser,
> + /*require_constexpr=*/true,
> + /*consumed_expr=*/NULL,
> + /*return_cond=*/false,
> + friend_p);
> if (type_id_list != NULL_TREE)
> return type_id_list;
>
> @@ -28403,7 +28613,7 @@ cp_parser_save_member_function_body (cp_parser* parser,
> return error_mark_node;
> }
>
> - /* Remember it, if there default args to post process. */
> + /* Remember it, if there are default args to post process. */
> cp_parser_save_default_args (parser, fn);
>
> /* Save away the tokens that make up the body of the
> @@ -28696,6 +28906,11 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
> vec_safe_push (unparsed_funs_with_default_args, entry);
> break;
> }
> +
> + /* Remember if there is a noexcept-specifier to post process. */
> + tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
> + if (UNPARSED_NOEXCEPT_SPEC_P (spec))
> + vec_safe_push (unparsed_noexcepts, decl);
> }
>
> /* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
> @@ -40506,7 +40721,11 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
> noex = NULL_TREE;
> }
> else
> - noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
> + noex = cp_parser_noexcept_specification_opt (parser,
> + /*require_constexpr=*/true,
> + /*consumed_expr=*/NULL,
> + /*return_cond=*/true,
> + /*friend_p=*/false);
>
> /* Keep track if we're in the lexical scope of an outer transaction. */
> new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
> @@ -40566,8 +40785,11 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
> parser->in_transaction = this_in;
>
> /* Parse a noexcept specification. */
> - noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr,
> - true);
> + noex = cp_parser_noexcept_specification_opt (parser,
> + /*require_constexpr=*/false,
> + &noex_expr,
> + /*return_cond=*/true,
> + /*friend_p=*/false);
>
> if (!noex || !noex_expr
> || cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
> diff --git gcc/cp/parser.h gcc/cp/parser.h
> index c03a9d87af5..2890788f489 100644
> --- gcc/cp/parser.h
> +++ gcc/cp/parser.h
> @@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
> /* Nested classes go in this vector, so that we can do some final
> processing after parsing any NSDMIs. */
> vec<tree, va_gc> *classes;
> +
> + /* Functions with noexcept-specifiers that require post-processing. */
> + vec<tree, va_gc> *noexcepts;
> };
>
>
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index cfbd9fd4c88..0324bd404e9 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -25302,8 +25302,9 @@ dependent_type_p_r (tree type)
> if (tree noex = TREE_PURPOSE (spec))
> /* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
> affect overload resolution and treating it as dependent breaks
> - things. */
> + things. Same for an unparsed noexcept expression. */
> if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
> + && TREE_CODE (noex) != DEFAULT_ARG
> && value_dependent_expression_p (noex))
> return true;
> return false;
> diff --git gcc/cp/search.c gcc/cp/search.c
> index dac08d44d76..372c4424747 100644
> --- gcc/cp/search.c
> +++ gcc/cp/search.c
> @@ -1860,6 +1860,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
> NULL, &lfd);
> }
>
> +/* Check throw specifier of OVERRIDER is at least as strict as
> + the one of BASEFN. */
> +
> +bool
> +maybe_check_overriding_exception_spec (tree overrider, tree basefn)
> +{
> + maybe_instantiate_noexcept (basefn);
> + maybe_instantiate_noexcept (overrider);
> + tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> + tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> +
> + if (DECL_INVALID_OVERRIDER_P (overrider))
> + return true;
> +
> + /* Can't check this yet. Pretend this is fine and let
> + noexcept_override_late_checks check this later. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
> + || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
> + return true;
> +
> + if (!comp_except_specs (base_throw, over_throw, ce_derived))
> + {
> + auto_diagnostic_group d;
> + error ("looser exception specification on overriding virtual function "
> + "%q+#F", overrider);
> + inform (DECL_SOURCE_LOCATION (basefn),
> + "overridden function is %q#F", basefn);
> + DECL_INVALID_OVERRIDER_P (overrider) = 1;
> + return false;
> + }
> + return true;
> +}
> +
> /* Check that virtual overrider OVERRIDER is acceptable for base function
> BASEFN. Issue diagnostic, and return zero, if unacceptable. */
>
> @@ -1870,7 +1903,6 @@ check_final_overrider (tree overrider, tree basefn)
> tree base_type = TREE_TYPE (basefn);
> tree over_return = fndecl_declared_return_type (overrider);
> tree base_return = fndecl_declared_return_type (basefn);
> - tree over_throw, base_throw;
>
> int fail = 0;
>
> @@ -1954,21 +1986,8 @@ check_final_overrider (tree overrider, tree basefn)
> return 0;
> }
>
> - /* Check throw specifier is at least as strict. */
> - maybe_instantiate_noexcept (basefn);
> - maybe_instantiate_noexcept (overrider);
> - base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
> - over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
> -
> - if (!comp_except_specs (base_throw, over_throw, ce_derived))
> - {
> - auto_diagnostic_group d;
> - error ("looser throw specifier for %q+#F", overrider);
> - inform (DECL_SOURCE_LOCATION (basefn),
> - "overridden function is %q#F", basefn);
> - DECL_INVALID_OVERRIDER_P (overrider) = 1;
> - return 0;
> - }
> + if (!maybe_check_overriding_exception_spec (overrider, basefn))
> + return 0;
>
> /* Check for conflicting type attributes. But leave transaction_safe for
> set_one_vmethod_tm_attributes. */
> diff --git gcc/cp/tree.c gcc/cp/tree.c
> index cd021b7f594..81c53b23ebf 100644
> --- gcc/cp/tree.c
> +++ gcc/cp/tree.c
> @@ -2546,6 +2546,7 @@ canonical_eh_spec (tree raises)
> if (raises == NULL_TREE)
> return raises;
> else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
> + || UNPARSED_NOEXCEPT_SPEC_P (raises)
> || uses_template_parms (raises)
> || uses_template_parms (TREE_PURPOSE (raises)))
> /* Keep a dependent or deferred exception specification. */
> @@ -3656,6 +3657,7 @@ cp_tree_equal (tree t1, tree t2)
> case IDENTIFIER_NODE:
> case SSA_NAME:
> case USING_DECL:
> + case DEFAULT_ARG:
> return false;
>
> case BASELINK:
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index e9f759d4213..eec5550bf2d 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -2391,6 +2391,15 @@ merge_exception_specifiers (tree list, tree add)
> if (list == error_mark_node || add == error_mark_node)
> return error_mark_node;
>
> + /* We don't want to lose the unparsed operand lest we miss diagnostics.
> + We can use the newer variant, because the old one will be saved in
> + DEFARG_DECL's noexcept-specifier. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (list))
> + return list;
> + else
> + /* Member functions can't be redeclared. */
> + gcc_assert (!UNPARSED_NOEXCEPT_SPEC_P (add));
> +
> /* No exception-specifier or noexcept(false) are less strict than
> anything else. Prefer the newer variant (LIST). */
> if (!list || list == noexcept_false_spec)
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept41.C gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> new file mode 100644
> index 00000000000..43b38c2446f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept41.C
> @@ -0,0 +1,147 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert(X, #X)
> +
> +struct S {
> + void f1() noexcept(noexcept(i)) { }
> + void f2() noexcept(noexcept(fn())) { }
> + void f3() noexcept(noexcept(fnx())) { }
> + void f4() noexcept(noexcept(i));
> + void f5() noexcept(noexcept(fn()));
> + void f6() noexcept(noexcept(fnx()));
> +
> + void f7() noexcept(1);
> + void f8() noexcept(0);
> + void f9() noexcept(b);
> + void f10() noexcept(!b);
> +
> + int i;
> + static constexpr auto b = true;
> + void fny() noexcept(noexcept(fn()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +S s;
> +SA(noexcept(s.f1()));
> +SA(!noexcept(s.f2()));
> +SA(noexcept(s.f3()));
> +SA(noexcept(s.f4()));
> +SA(!noexcept(s.f5()));
> +SA(noexcept(s.f6()));
> +SA(noexcept(s.f7()));
> +SA(!noexcept(s.f8()));
> +SA(noexcept(s.f9()));
> +SA(!noexcept(s.f10()));
> +
> +struct S2 {
> + struct V {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void f3() noexcept(noexcept(fn())) { }
> + void f4() noexcept(noexcept(fnx())) { }
> + void fn();
> + void fnx() noexcept;
> + } v;
> + void fn();
> + void fnx();
> +};
> +
> +S2 s2;
> +SA(!noexcept(s2.v.f1()));
> +SA(noexcept(s2.v.f2()));
> +SA(!noexcept(s2.v.f3()));
> +SA(noexcept(s2.v.f4()));
> +
> +struct S3 {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +void
> +S3::f1() noexcept(noexcept(fn()))
> +{
> +}
> +
> +void
> +S3::f2() noexcept(noexcept(fnx()))
> +{
> +}
> +
> +struct S4 {
> + int f1 (int p) noexcept(noexcept(p)) { return p; }
> + int f2 (int p) noexcept(noexcept(p));
> + int f3 (int p = 10) noexcept(noexcept(p));
> + int f4 () noexcept(noexcept(S4{}));
> +};
> +
> +S4 s4;
> +SA(noexcept(s4.f1(1)));
> +SA(noexcept(s4.f2(1)));
> +SA(noexcept(s4.f3()));
> +SA(noexcept(s4.f4()));
> +
> +template<typename T>
> +struct S5 {
> + void f1() noexcept(noexcept(i)) { }
> + void f2() noexcept(noexcept(fn())) { }
> + void f3() noexcept(noexcept(fnx())) { }
> + void f4() noexcept(noexcept(i));
> + void f5() noexcept(noexcept(fn()));
> + void f6() noexcept(noexcept(fnx()));
> +
> + int i;
> + void fny() noexcept(noexcept(fn()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +S5<int> s5;
> +SA(noexcept(s5.f1()));
> +SA(!noexcept(s5.f2()));
> +SA(noexcept(s5.f3()));
> +SA(noexcept(s5.f4()));
> +SA(!noexcept(s5.f5()));
> +SA(noexcept(s5.f6()));
> +
> +template<typename T>
> +struct S6 {
> + void f1() noexcept(noexcept(x));
> + T x;
> +};
> +
> +struct S7 {
> + template<typename U>
> + void f1 () noexcept(noexcept(U(1))) { }
> +
> + template<int N>
> + void f2() noexcept(noexcept(N));
> +
> + template <typename _Up>
> + void f3(_Up __p) noexcept(noexcept(__p));
> +};
> +
> +void glob();
> +void globx() noexcept;
> +struct S8 {
> + void f1 () noexcept(noexcept(glob()));
> + void f2 () noexcept(noexcept(globx()));
> +};
> +
> +S8 s8;
> +SA(!noexcept(s8.f1()));
> +SA(noexcept(s8.f2()));
> +
> +struct W {
> + constexpr operator bool();
> +};
> +
> +template<typename T>
> +struct S9 {
> + S9() noexcept(noexcept(w)) { }
> + S9 &operator=(S9 &&) noexcept(T::X);
> + W w;
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> new file mode 100644
> index 00000000000..b3859de9ebc
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
> @@ -0,0 +1,26 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +struct S {
> + void f1() noexcept(noexcept(fn()));
> + void f2() noexcept(noexcept(fnx()));
> + void fn();
> + void fnx() noexcept;
> +};
> +
> +void
> +S::f1() noexcept // { dg-error "different exception specifier" }
> +{
> +}
> +
> +void
> +S::f2() // { dg-error "different exception specifier" }
> +{
> +}
> +
> +struct S2 {
> + void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
> + void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
> + void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
> + void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> new file mode 100644
> index 00000000000..12c6d364099
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
> @@ -0,0 +1,9 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +template <typename _Alloc> class A {
> + typedef _Alloc _Alloc_traits;
> + A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> + A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
> + void m_fn2(A<char>) {}
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> new file mode 100644
> index 00000000000..a81032f28e9
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
> @@ -0,0 +1,14 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +void fn1(void());
> +template <typename> class A {
> + void _M_local_data();
> + A() noexcept(_M_local_data);
> +};
> +
> +class B {
> + void _S_initialize();
> + static void _S_initialize_once();
> +};
> +void B::_S_initialize() { fn1(_S_initialize_once); }
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> new file mode 100644
> index 00000000000..39df4a6571e
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
> @@ -0,0 +1,23 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +struct A
> +{
> + virtual void f();
> + virtual void g() noexcept;
> + virtual void h() noexcept(false);
> +};
> +
> +struct B : A
> +{
> + void f() noexcept(true);
> + void g() noexcept(true);
> + void h() noexcept(true);
> +};
> +
> +struct D : A
> +{
> + void f() noexcept(false);
> + void g() noexcept(false); // { dg-error "looser exception specification" }
> + void h() noexcept(false);
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> new file mode 100644
> index 00000000000..da7490d651c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
> @@ -0,0 +1,28 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +void f() noexcept(false);
> +void g() noexcept(true);
> +void h() noexcept;
> +
> +struct B {
> + friend void f() noexcept(false);
> + friend void g() noexcept(false); // { dg-error "different exception specifier" }
> + friend void h() noexcept(false); // { dg-error "different exception specifier" }
> +};
> +
> +struct C {
> + friend void f() noexcept(true); // { dg-error "different exception specifier" }
> + friend void g() noexcept(true); // { dg-error "different exception specifier" }
> + friend void h() noexcept(true); // { dg-error "different exception specifier" }
> +};
> +
> +void o() noexcept(false);
> +void p() noexcept(true);
> +void q() noexcept;
> +
> +struct D {
> + friend void o() noexcept(true); // { dg-error "different exception specifier" }
> + friend void p() noexcept(true);
> + friend void q() noexcept(true);
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
> new file mode 100644
> index 00000000000..0848e68f9b1
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
> @@ -0,0 +1,83 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +int fn1 ();
> +int fn2 () noexcept;
> +int fn3 () noexcept;
> +
> +void g() noexcept(noexcept (fn2()));
> +
> +struct S1 {
> + friend void g1() noexcept(noexcept(fn2()));
> + friend void g1() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
> +};
> +
> +struct S2 {
> + friend void g2() noexcept(noexcept(fn1()));
> + friend void g2() noexcept(noexcept(fn1()));
> + friend void g2() noexcept(noexcept(fn1()));
> +};
> +
> +struct S3 {
> + friend void g3() noexcept(noexcept(fn1()));
> + friend void g3() noexcept(noexcept(fn3())); // { dg-error "different exception specifier" }
> +};
> +
> +struct S4 {
> + friend void g4() noexcept(noexcept(fn2()));
> + friend void g4() noexcept(noexcept(fn3()));
> +};
> +
> +struct S5 {
> + friend void g() noexcept(noexcept(fn3()));
> +};
> +
> +struct S6 {
> + friend void g() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
> +};
> +
> +struct S7 {
> + friend void gg() noexcept(noexcept(fn3()));
> +};
> +
> +void gg() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
> +
> +struct S8 {
> + friend void g8();
> + friend void g8() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
> +};
> +
> +struct S9 {
> + friend void g9();
> + friend void g9() noexcept(noexcept(fn1()));
> +};
> +
> +struct S10 {
> + friend void g10() noexcept(noexcept(fn1()));
> + friend void g10();
> +};
> +
> +struct S11 {
> + friend void g11() noexcept(noexcept(fn2()));
> + friend void g11(); // { dg-error "different exception specifier" }
> +};
> +
> +struct S12 {
> + friend void g12() noexcept(false);
> + friend void g12() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
> +};
> +
> +struct S13 {
> + friend void g13() noexcept(false);
> + friend void g13() noexcept(noexcept(fn1()));
> +};
> +
> +struct S14 {
> + friend void g14() noexcept(noexcept(fn1()));
> + friend void g14() noexcept(false);
> +};
> +
> +struct S15 {
> + friend void g15() noexcept(noexcept(fn2()));
> + friend void g15() noexcept(false); // { dg-error "different exception specifier" }
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
> new file mode 100644
> index 00000000000..134212c3613
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
> @@ -0,0 +1,11 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +int g;
> +
> +struct S {
> + int b;
> + friend void fn1(int n) noexcept(noexcept(n));
> + friend void fn2() noexcept(noexcept(g));
> + friend void fn3() noexcept(noexcept(b));
> +};
> diff --git gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
> new file mode 100644
> index 00000000000..6da7ff3361f
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
> @@ -0,0 +1,12 @@
> +// PR c++/86476 - noexcept-specifier is a complete-class context
> +// { dg-do compile { target c++11 } }
> +
> +#define SA(X) static_assert(X, #X)
> +
> +struct S {
> + static void f1() noexcept(b);
> + static constexpr auto b = true;
> +};
> +
> +S s;
> +SA(noexcept(s.f1()));
> diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
> index 0ba6145ef0c..6bccc704d49 100644
> --- gcc/testsuite/g++.dg/eh/shadow1.C
> +++ gcc/testsuite/g++.dg/eh/shadow1.C
> @@ -18,7 +18,7 @@ struct D : private B
> // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
> struct E : public D
> {
> - virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
> + virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
> }; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
> // { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
> B* foo (D *);
Marek
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-06-04 1:02 ` Marek Polacek
2019-06-10 12:28 ` Marek Polacek
@ 2019-06-12 3:46 ` Jason Merrill
2019-06-14 20:54 ` Marek Polacek
1 sibling, 1 reply; 14+ messages in thread
From: Jason Merrill @ 2019-06-12 3:46 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 6/3/19 9:01 PM, Marek Polacek wrote:
> I sort of ended up going down a rathole, but then I realized we don't need to
> delay parsing of noexcept-specifiers of member friend function declarations,
> because they aren't members of the class.
Where are you getting this from? I'm definitely sympathetic to the idea
that noexcept-specifiers of friend functions shouldn't need to be
complete-class contexts, but 10.3 doesn't make that distinction that I
can see.
> This was a huge relief because
> member friend function declarations can be redeclared, so we'd have to make
> sure to check if their noexcept-specifiers match. But member function decls
> can't be redeclared. I updated the comment to better reflect why what I'm
> doing there is correct, along with an assert.
But then why do you still need this:
> + /* We can't compare unparsed noexcept-specifiers. Save the decl
> + and check this again after we've parsed the noexcept-specifiers
> + for real. */
> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> + {
> + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> + return;
> + }
?
Jason
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-06-12 3:46 ` Jason Merrill
@ 2019-06-14 20:54 ` Marek Polacek
2019-06-21 20:47 ` Jason Merrill
0 siblings, 1 reply; 14+ messages in thread
From: Marek Polacek @ 2019-06-14 20:54 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
> On 6/3/19 9:01 PM, Marek Polacek wrote:
>
> > I sort of ended up going down a rathole, but then I realized we don't need to
> > delay parsing of noexcept-specifiers of member friend function declarations,
> > because they aren't members of the class.
>
> Where are you getting this from? I'm definitely sympathetic to the idea
> that noexcept-specifiers of friend functions shouldn't need to be
> complete-class contexts, but 10.3 doesn't make that distinction that I can
> see.
When I tested my patch I noticed that none of the 3 compilers I tried handled
this scenario, so I thought I was missing something. But if the standard
really doesn't say that noexcept-specifiers of friend functions don't have to
be complete-class contexts, then perhaps it needs to say so. Should I raise
this on the reflector?
> > This was a huge relief because
> > member friend function declarations can be redeclared, so we'd have to make
> > sure to check if their noexcept-specifiers match. But member function decls
> > can't be redeclared. I updated the comment to better reflect why what I'm
> > doing there is correct, along with an assert.
>
> But then why do you still need this:
>
> > + /* We can't compare unparsed noexcept-specifiers. Save the decl
> > + and check this again after we've parsed the noexcept-specifiers
> > + for real. */
> > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > + {
> > + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> > + return;
> > + }
>
> ?
Eh... I don't. The following version is with the DEFARG_DECL junk removed.
Bootstrapped/regtested on x86_64-linux, ok for trunk?
2019-06-14 Marek Polacek <polacek@redhat.com>
PR c++/86476 - noexcept-specifier is a complete-class context.
PR c++/52869
* cp-tree.def (DEFAULT_ARG): Update commentary.
* cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro.
(tree_default_arg): Use tree_base instead of tree_common.
(do_push_parm_decls, maybe_check_overriding_exception_spec): Declare.
* decl.c (do_push_parm_decls): New function, broken out of...
(store_parm_decls): ...here. Call it.
* except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
* parser.c (cp_parser_noexcept_specification_opt,
cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
Forward-declare.
(unparsed_noexcepts): New macro.
(push_unparsed_function_queues): Update initializer.
(cp_parser_direct_declarator): Pass FRIEND_P to
cp_parser_exception_specification_opt.
(inject_parm_decls): New.
(pop_injected_parms): New.
(cp_parser_class_specifier_1): Implement delayed parsing of
noexcept-specifiers.
(cp_parser_save_noexcept): New.
(cp_parser_late_noexcept_specifier): New.
(noexcept_override_late_checks): New.
(cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call
cp_parser_save_noexcept instead of the normal processing if needed.
(cp_parser_exception_specification_opt): Add FRIEND_P parameter and
pass it to cp_parser_noexcept_specification_opt.
(cp_parser_save_member_function_body): Fix comment.
(cp_parser_save_default_args): Maybe save the noexcept-specifier to
post process.
(cp_parser_transaction): Update call to
cp_parser_noexcept_specification_opt.
(cp_parser_transaction_expression): Likewise.
* parser.h (cp_unparsed_functions_entry): Add new field to carry
a noexcept-specifier.
* pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
* search.c (maybe_check_overriding_exception_spec): New function, broken
out of...
(check_final_overrider): ...here. Call
maybe_check_overriding_exception_spec.
* tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
(cp_tree_equal): Handle DEFAULT_ARG.
* g++.dg/cpp0x/noexcept42.C: New test.
* g++.dg/cpp0x/noexcept43.C: New test.
* g++.dg/cpp0x/noexcept44.C: New test.
* g++.dg/cpp0x/noexcept45.C: New test.
* g++.dg/cpp0x/noexcept46.C: New test.
* g++.dg/cpp0x/noexcept47.C: New test.
* g++.dg/cpp0x/noexcept48.C: New test.
* g++.dg/cpp0x/noexcept49.C: New test.
* g++.dg/cpp0x/noexcept50.C: New test.
* g++.dg/eh/shadow1.C: Adjust dg-error.
diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 03c105b5c4c..475c584fd4c 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -209,7 +209,8 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
/* An un-parsed default argument. Holds a vector of input tokens and
a vector of places where the argument was instantiated before
- parsing had occurred. */
+ parsing had occurred. This is also used for delayed NSDMIs and
+ noexcept-specifier parsing. */
DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
/* An uninstantiated/unevaluated noexcept-specification. For the
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 1f4e1e15554..107a322cc82 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1190,7 +1190,7 @@ enum cp_identifier_kind {
(((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
struct GTY (()) tree_default_arg {
- struct tree_common common;
+ struct tree_base base;
struct cp_token_cache *tokens;
vec<tree, va_gc> *instantiations;
};
@@ -1206,6 +1206,9 @@ struct GTY (()) tree_default_arg {
#define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
(DEFERRED_NOEXCEPT_SPEC_P (NODE) \
&& DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
+#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
+ ((NODE) && (TREE_PURPOSE (NODE)) \
+ && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
struct GTY (()) tree_deferred_noexcept {
struct tree_base base;
@@ -6467,6 +6470,7 @@ extern bool check_array_designated_initializer (constructor_elt *,
unsigned HOST_WIDE_INT);
extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
extern tree build_explicit_specifier (tree, tsubst_flags_t);
+extern void do_push_parm_decls (tree, tree, tree *);
/* in decl2.c */
extern void record_mangling (tree, bool);
@@ -6932,6 +6936,7 @@ extern tree copied_binfo (tree, tree);
extern tree original_binfo (tree, tree);
extern int shared_member_p (tree);
extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
+extern bool maybe_check_overriding_exception_spec (tree, tree);
/* The representation of a deferred access check. */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 0a3ef452536..43d7ba6a114 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -15720,6 +15720,39 @@ use_eh_spec_block (tree fn)
&& !DECL_DEFAULTED_FN (fn));
}
+/* Helper function to push ARGS into the current lexical scope. DECL
+ is the function declaration. NONPARMS is used to handle enum
+ constants. */
+
+void
+do_push_parm_decls (tree decl, tree args, tree *nonparms)
+{
+ /* If we're doing semantic analysis, then we'll call pushdecl
+ for each of these. We must do them in reverse order so that
+ they end in the correct forward order. */
+ args = nreverse (args);
+
+ tree next;
+ for (tree parm = args; parm; parm = next)
+ {
+ next = DECL_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ pushdecl (parm);
+ else if (nonparms)
+ {
+ /* If we find an enum constant or a type tag, put it aside for
+ the moment. */
+ TREE_CHAIN (parm) = NULL_TREE;
+ *nonparms = chainon (*nonparms, parm);
+ }
+ }
+
+ /* Get the decls in their original chain order and record in the
+ function. This is all and only the PARM_DECLs that were
+ pushed into scope by the loop above. */
+ DECL_ARGUMENTS (decl) = get_local_decls ();
+}
+
/* Store the parameter declarations into the current function declaration.
This is called after parsing the parameter declarations, before
digesting the body of the function.
@@ -15730,7 +15763,6 @@ static void
store_parm_decls (tree current_function_parms)
{
tree fndecl = current_function_decl;
- tree parm;
/* This is a chain of any other decls that came in among the parm
declarations. If a parm is declared with enum {foo, bar} x;
@@ -15745,35 +15777,12 @@ store_parm_decls (tree current_function_parms)
and complain if any redundant old-style parm decls were written. */
tree specparms = current_function_parms;
- tree next;
/* Must clear this because it might contain TYPE_DECLs declared
at class level. */
current_binding_level->names = NULL;
- /* If we're doing semantic analysis, then we'll call pushdecl
- for each of these. We must do them in reverse order so that
- they end in the correct forward order. */
- specparms = nreverse (specparms);
-
- for (parm = specparms; parm; parm = next)
- {
- next = DECL_CHAIN (parm);
- if (TREE_CODE (parm) == PARM_DECL)
- pushdecl (parm);
- else
- {
- /* If we find an enum constant or a type tag,
- put it aside for the moment. */
- TREE_CHAIN (parm) = NULL_TREE;
- nonparms = chainon (nonparms, parm);
- }
- }
-
- /* Get the decls in their original chain order and record in the
- function. This is all and only the PARM_DECLs that were
- pushed into scope by the loop above. */
- DECL_ARGUMENTS (fndecl) = get_local_decls ();
+ do_push_parm_decls (fndecl, specparms, &nonparms);
}
else
DECL_ARGUMENTS (fndecl) = NULL_TREE;
diff --git gcc/cp/except.c gcc/cp/except.c
index 892d5201da9..8d7b1e9bac7 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
|| TREE_VALUE (spec)
|| spec == noexcept_false_spec
|| TREE_PURPOSE (spec) == error_mark_node
+ || UNPARSED_NOEXCEPT_SPEC_P (spec)
|| processing_template_decl);
return false;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 8f5ae84670a..0d926cc0bd5 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
static cp_token_cache *cp_token_cache_new
(cp_token *, cp_token *);
+static tree cp_parser_noexcept_specification_opt
+ (cp_parser *, bool, bool *, bool, bool);
+static tree cp_parser_late_noexcept_specifier
+ (cp_parser *, tree);
+static void noexcept_override_late_checks
+ (tree, tree);
static void cp_parser_initial_pragma
(cp_token *);
@@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_classes \
parser->unparsed_queues->last ().classes
+#define unparsed_noexcepts \
+ parser->unparsed_queues->last ().noexcepts
static void
push_unparsed_function_queues (cp_parser *parser)
{
- cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
+ cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
+ NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -2361,7 +2370,7 @@ static tree cp_parser_exception_declaration
static tree cp_parser_throw_expression
(cp_parser *);
static tree cp_parser_exception_specification_opt
- (cp_parser *);
+ (cp_parser *, bool = false);
static tree cp_parser_type_id_list
(cp_parser *);
@@ -20816,7 +20825,7 @@ cp_parser_direct_declarator (cp_parser* parser,
tree tx_qual = cp_parser_tx_qualifier_opt (parser);
/* And the exception-specification. */
exception_specification
- = cp_parser_exception_specification_opt (parser);
+ = cp_parser_exception_specification_opt (parser, friend_p);
attrs = cp_parser_std_attribute_spec_seq (parser);
@@ -23310,6 +23319,34 @@ cp_parser_class_name (cp_parser *parser,
return decl;
}
+/* Make sure that any member-function parameters are in scope.
+ For instance, a function's noexcept-specifier can use the function's
+ parameters:
+
+ struct S {
+ void fn (int p) noexcept(noexcept(p));
+ };
+
+ so we need to make sure name lookup can find them. This is used
+ when we delay parsing of the noexcept-specifier. */
+
+static void
+inject_parm_decls (tree decl)
+{
+ begin_scope (sk_function_parms, decl);
+ tree args = DECL_ARGUMENTS (decl);
+
+ do_push_parm_decls (decl, args, /*nonparms=*/NULL);
+}
+
+/* Undo the effects of inject_parm_decls. */
+
+static void
+pop_injected_parms (void)
+{
+ pop_bindings_and_leave_scope ();
+}
+
/* Parse a class-specifier.
class-specifier:
@@ -23620,6 +23657,60 @@ cp_parser_class_specifier_1 (cp_parser* parser)
vec_safe_truncate (unparsed_classes, 0);
after_nsdmi_defaulted_late_checks (type);
+ /* If there are noexcept-specifiers that have not yet been processed,
+ take care of them now. */
+ class_type = NULL_TREE;
+ pushed_scope = NULL_TREE;
+ FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+ {
+ tree ctx = DECL_CONTEXT (decl);
+ if (class_type != ctx)
+ {
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+ class_type = ctx;
+ pushed_scope = push_scope (class_type);
+ }
+
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ spec = TREE_PURPOSE (spec);
+
+ /* Make sure that any template parameters are in scope. */
+ maybe_begin_member_template_processing (decl);
+
+ /* Make sure that any member-function parameters are in scope. */
+ inject_parm_decls (decl);
+
+ /* 'this' is not allowed in static member functions. */
+ unsigned char local_variables_forbidden_p
+ = parser->local_variables_forbidden_p;
+ if (DECL_THIS_STATIC (decl))
+ parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+
+ /* Now we can parse the noexcept-specifier. */
+ spec = cp_parser_late_noexcept_specifier (parser, spec);
+
+ if (spec != error_mark_node)
+ TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
+
+ /* Restore the state of local_variables_forbidden_p. */
+ parser->local_variables_forbidden_p = local_variables_forbidden_p;
+
+ /* The finish_struct call above performed various override checking,
+ but it skipped unparsed noexcept-specifier operands. Now that we
+ have resolved them, check again. */
+ noexcept_override_late_checks (type, decl);
+
+ /* Remove any member-function parameters from the symbol table. */
+ pop_injected_parms ();
+
+ /* Remove any template parameters from the symbol table. */
+ maybe_end_member_template_processing ();
+ }
+ vec_safe_truncate (unparsed_noexcepts, 0);
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+
/* Now parse the body of the functions. */
if (flag_openmp)
{
@@ -25175,6 +25266,89 @@ cp_parser_base_specifier (cp_parser* parser)
/* Exception handling [gram.exception] */
+/* Save the tokens that make up the noexcept-specifier for a member-function.
+ Returns a DEFAULT_ARG. */
+
+static tree
+cp_parser_save_noexcept (cp_parser *parser)
+{
+ cp_token *first = parser->lexer->next_token;
+ /* We want everything up to, including, the final ')'. */
+ cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
+ cp_token *last = parser->lexer->next_token;
+
+ /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
+ to carry the information we will need. */
+ tree expr = make_node (DEFAULT_ARG);
+ /* Save away the noexcept-specifier; we will process it when the
+ class is complete. */
+ DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
+ expr = build_tree_list (expr, NULL_TREE);
+ return expr;
+}
+
+/* Used for late processing of noexcept-specifiers of member-functions.
+ DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
+ we saved for later; parse it now. */
+
+static tree
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+{
+ /* Make sure we've gotten something that hasn't been parsed yet. */
+ gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
+
+ push_unparsed_function_queues (parser);
+
+ /* Push the saved tokens for the noexcept-specifier onto the parser's
+ lexer stack. */
+ cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
+ cp_parser_push_lexer_for_tokens (parser, tokens);
+
+ /* Parse the cached noexcept-specifier. */
+ tree parsed_arg
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/false,
+ /*friend_p=*/false);
+
+ /* Revert to the main lexer. */
+ cp_parser_pop_lexer (parser);
+
+ /* Restore the queue. */
+ pop_unparsed_function_queues (parser);
+
+ /* And we're done. */
+ return parsed_arg;
+}
+
+/* Perform late checking of overriding function with respect to their
+ noexcept-specifiers. TYPE is the class and FNDECL is the function
+ that potentially overrides some virtual function with the same
+ signature. */
+
+static void
+noexcept_override_late_checks (tree type, tree fndecl)
+{
+ tree binfo = TYPE_BINFO (type);
+ tree base_binfo;
+
+ if (DECL_STATIC_FUNCTION_P (fndecl))
+ return;
+
+ for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+ {
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ if (!TYPE_POLYMORPHIC_P (basetype))
+ continue;
+
+ tree fn = look_for_overrides_here (basetype, fndecl);
+ if (fn)
+ maybe_check_overriding_exception_spec (fndecl, fn);
+ }
+}
+
/* Parse an (optional) noexcept-specification.
noexcept-specification:
@@ -25185,13 +25359,15 @@ cp_parser_base_specifier (cp_parser* parser)
expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
there are no parentheses. CONSUMED_EXPR will be set accordingly.
Otherwise, returns a noexcept specification unless RETURN_COND is true,
- in which case a boolean condition is returned instead. */
+ in which case a boolean condition is returned instead. If FRIEND_P is true,
+ the function with this noexcept-specification had the `friend' specifier. */
static tree
cp_parser_noexcept_specification_opt (cp_parser* parser,
bool require_constexpr,
bool* consumed_expr,
- bool return_cond)
+ bool return_cond,
+ bool friend_p)
{
cp_token *token;
const char *saved_message;
@@ -25203,6 +25379,26 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
if (cp_parser_is_keyword (token, RID_NOEXCEPT))
{
tree expr;
+
+ /* [class.mem]/6 says that a noexcept-specifer (within the
+ member-specification of the class) is a complete-class context of
+ a class. So, if the noexcept-specifier has the optional expression,
+ just save the tokens, and reparse this after we're done with the
+ class. */
+ if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+ /* No need to delay parsing for a number literal or true/false. */
+ && !cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
+ && !(cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)
+ && (cp_lexer_nth_token_is_keyword (parser->lexer, 3, RID_FALSE)
+ || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
+ RID_TRUE)))
+ && at_class_scope_p ()
+ /* Don't delay parsing for friend member functions. */
+ && !friend_p
+ && TYPE_BEING_DEFINED (current_class_type)
+ && !LAMBDA_TYPE_P (current_class_type))
+ return cp_parser_save_noexcept (parser);
+
cp_lexer_consume_token (parser->lexer);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
@@ -25273,10 +25469,11 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
throw ( type-id-list [opt] )
Returns a TREE_LIST representing the exception-specification. The
- TREE_VALUE of each node is a type. */
+ TREE_VALUE of each node is a type. If FRIEND_P is true, the function
+ with this noexcept-specification had the `friend' specifier. */
static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
{
cp_token *token;
tree type_id_list;
@@ -25286,8 +25483,12 @@ cp_parser_exception_specification_opt (cp_parser* parser)
token = cp_lexer_peek_token (parser->lexer);
/* Is it a noexcept-specification? */
- type_id_list = cp_parser_noexcept_specification_opt (parser, true, NULL,
- false);
+ type_id_list
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/false,
+ friend_p);
if (type_id_list != NULL_TREE)
return type_id_list;
@@ -28415,7 +28616,7 @@ cp_parser_save_member_function_body (cp_parser* parser,
return error_mark_node;
}
- /* Remember it, if there default args to post process. */
+ /* Remember it, if there are default args to post process. */
cp_parser_save_default_args (parser, fn);
/* Save away the tokens that make up the body of the
@@ -28708,6 +28909,11 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
vec_safe_push (unparsed_funs_with_default_args, entry);
break;
}
+
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
}
/* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
@@ -40579,7 +40785,11 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
noex = NULL_TREE;
}
else
- noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
+ noex = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/true,
+ /*friend_p=*/false);
/* Keep track if we're in the lexical scope of an outer transaction. */
new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -40639,8 +40849,11 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
parser->in_transaction = this_in;
/* Parse a noexcept specification. */
- noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr,
- true);
+ noex = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/false,
+ &noex_expr,
+ /*return_cond=*/true,
+ /*friend_p=*/false);
if (!noex || !noex_expr
|| cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git gcc/cp/parser.h gcc/cp/parser.h
index c03a9d87af5..2890788f489 100644
--- gcc/cp/parser.h
+++ gcc/cp/parser.h
@@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Nested classes go in this vector, so that we can do some final
processing after parsing any NSDMIs. */
vec<tree, va_gc> *classes;
+
+ /* Functions with noexcept-specifiers that require post-processing. */
+ vec<tree, va_gc> *noexcepts;
};
diff --git gcc/cp/pt.c gcc/cp/pt.c
index 2a626526c6f..05bc4a3546a 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -25307,8 +25307,9 @@ dependent_type_p_r (tree type)
if (tree noex = TREE_PURPOSE (spec))
/* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
affect overload resolution and treating it as dependent breaks
- things. */
+ things. Same for an unparsed noexcept expression. */
if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
+ && TREE_CODE (noex) != DEFAULT_ARG
&& value_dependent_expression_p (noex))
return true;
return false;
diff --git gcc/cp/search.c gcc/cp/search.c
index dac08d44d76..372c4424747 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -1860,6 +1860,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
NULL, &lfd);
}
+/* Check throw specifier of OVERRIDER is at least as strict as
+ the one of BASEFN. */
+
+bool
+maybe_check_overriding_exception_spec (tree overrider, tree basefn)
+{
+ maybe_instantiate_noexcept (basefn);
+ maybe_instantiate_noexcept (overrider);
+ tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+ tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
+ if (DECL_INVALID_OVERRIDER_P (overrider))
+ return true;
+
+ /* Can't check this yet. Pretend this is fine and let
+ noexcept_override_late_checks check this later. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
+ || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
+ return true;
+
+ if (!comp_except_specs (base_throw, over_throw, ce_derived))
+ {
+ auto_diagnostic_group d;
+ error ("looser exception specification on overriding virtual function "
+ "%q+#F", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %q#F", basefn);
+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
+ return false;
+ }
+ return true;
+}
+
/* Check that virtual overrider OVERRIDER is acceptable for base function
BASEFN. Issue diagnostic, and return zero, if unacceptable. */
@@ -1870,7 +1903,6 @@ check_final_overrider (tree overrider, tree basefn)
tree base_type = TREE_TYPE (basefn);
tree over_return = fndecl_declared_return_type (overrider);
tree base_return = fndecl_declared_return_type (basefn);
- tree over_throw, base_throw;
int fail = 0;
@@ -1954,21 +1986,8 @@ check_final_overrider (tree overrider, tree basefn)
return 0;
}
- /* Check throw specifier is at least as strict. */
- maybe_instantiate_noexcept (basefn);
- maybe_instantiate_noexcept (overrider);
- base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
- over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
-
- if (!comp_except_specs (base_throw, over_throw, ce_derived))
- {
- auto_diagnostic_group d;
- error ("looser throw specifier for %q+#F", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#F", basefn);
- DECL_INVALID_OVERRIDER_P (overrider) = 1;
- return 0;
- }
+ if (!maybe_check_overriding_exception_spec (overrider, basefn))
+ return 0;
/* Check for conflicting type attributes. But leave transaction_safe for
set_one_vmethod_tm_attributes. */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index cd021b7f594..81c53b23ebf 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2546,6 +2546,7 @@ canonical_eh_spec (tree raises)
if (raises == NULL_TREE)
return raises;
else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+ || UNPARSED_NOEXCEPT_SPEC_P (raises)
|| uses_template_parms (raises)
|| uses_template_parms (TREE_PURPOSE (raises)))
/* Keep a dependent or deferred exception specification. */
@@ -3656,6 +3657,7 @@ cp_tree_equal (tree t1, tree t2)
case IDENTIFIER_NODE:
case SSA_NAME:
case USING_DECL:
+ case DEFAULT_ARG:
return false;
case BASELINK:
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept42.C gcc/testsuite/g++.dg/cpp0x/noexcept42.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept42.C
@@ -0,0 +1,26 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct S {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S::f1() noexcept // { dg-error "different exception specifier" }
+{
+}
+
+void
+S::f2() // { dg-error "different exception specifier" }
+{
+}
+
+struct S2 {
+ void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
+ void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
+ void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
+ void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept43.C gcc/testsuite/g++.dg/cpp0x/noexcept43.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept43.C
@@ -0,0 +1,9 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+template <typename _Alloc> class A {
+ typedef _Alloc _Alloc_traits;
+ A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ void m_fn2(A<char>) {}
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept44.C gcc/testsuite/g++.dg/cpp0x/noexcept44.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept44.C
@@ -0,0 +1,14 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void fn1(void());
+template <typename> class A {
+ void _M_local_data();
+ A() noexcept(_M_local_data);
+};
+
+class B {
+ void _S_initialize();
+ static void _S_initialize_once();
+};
+void B::_S_initialize() { fn1(_S_initialize_once); }
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
new file mode 100644
index 00000000000..39df4a6571e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
@@ -0,0 +1,23 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+ virtual void f();
+ virtual void g() noexcept;
+ virtual void h() noexcept(false);
+};
+
+struct B : A
+{
+ void f() noexcept(true);
+ void g() noexcept(true);
+ void h() noexcept(true);
+};
+
+struct D : A
+{
+ void f() noexcept(false);
+ void g() noexcept(false); // { dg-error "looser exception specification" }
+ void h() noexcept(false);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
new file mode 100644
index 00000000000..da7490d651c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
@@ -0,0 +1,28 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void f() noexcept(false);
+void g() noexcept(true);
+void h() noexcept;
+
+struct B {
+ friend void f() noexcept(false);
+ friend void g() noexcept(false); // { dg-error "different exception specifier" }
+ friend void h() noexcept(false); // { dg-error "different exception specifier" }
+};
+
+struct C {
+ friend void f() noexcept(true); // { dg-error "different exception specifier" }
+ friend void g() noexcept(true); // { dg-error "different exception specifier" }
+ friend void h() noexcept(true); // { dg-error "different exception specifier" }
+};
+
+void o() noexcept(false);
+void p() noexcept(true);
+void q() noexcept;
+
+struct D {
+ friend void o() noexcept(true); // { dg-error "different exception specifier" }
+ friend void p() noexcept(true);
+ friend void q() noexcept(true);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644
index 00000000000..0848e68f9b1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
@@ -0,0 +1,83 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int fn1 ();
+int fn2 () noexcept;
+int fn3 () noexcept;
+
+void g() noexcept(noexcept (fn2()));
+
+struct S1 {
+ friend void g1() noexcept(noexcept(fn2()));
+ friend void g1() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S2 {
+ friend void g2() noexcept(noexcept(fn1()));
+ friend void g2() noexcept(noexcept(fn1()));
+ friend void g2() noexcept(noexcept(fn1()));
+};
+
+struct S3 {
+ friend void g3() noexcept(noexcept(fn1()));
+ friend void g3() noexcept(noexcept(fn3())); // { dg-error "different exception specifier" }
+};
+
+struct S4 {
+ friend void g4() noexcept(noexcept(fn2()));
+ friend void g4() noexcept(noexcept(fn3()));
+};
+
+struct S5 {
+ friend void g() noexcept(noexcept(fn3()));
+};
+
+struct S6 {
+ friend void g() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S7 {
+ friend void gg() noexcept(noexcept(fn3()));
+};
+
+void gg() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+
+struct S8 {
+ friend void g8();
+ friend void g8() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S9 {
+ friend void g9();
+ friend void g9() noexcept(noexcept(fn1()));
+};
+
+struct S10 {
+ friend void g10() noexcept(noexcept(fn1()));
+ friend void g10();
+};
+
+struct S11 {
+ friend void g11() noexcept(noexcept(fn2()));
+ friend void g11(); // { dg-error "different exception specifier" }
+};
+
+struct S12 {
+ friend void g12() noexcept(false);
+ friend void g12() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S13 {
+ friend void g13() noexcept(false);
+ friend void g13() noexcept(noexcept(fn1()));
+};
+
+struct S14 {
+ friend void g14() noexcept(noexcept(fn1()));
+ friend void g14() noexcept(false);
+};
+
+struct S15 {
+ friend void g15() noexcept(noexcept(fn2()));
+ friend void g15() noexcept(false); // { dg-error "different exception specifier" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644
index 00000000000..134212c3613
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
@@ -0,0 +1,11 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int g;
+
+struct S {
+ int b;
+ friend void fn1(int n) noexcept(noexcept(n));
+ friend void fn2() noexcept(noexcept(g));
+ friend void fn3() noexcept(noexcept(b));
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644
index 00000000000..6da7ff3361f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
@@ -0,0 +1,12 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ static void f1() noexcept(b);
+ static constexpr auto b = true;
+};
+
+S s;
+SA(noexcept(s.f1()));
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept50.C gcc/testsuite/g++.dg/cpp0x/noexcept50.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept50.C
@@ -0,0 +1,147 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ void f7() noexcept(1);
+ void f8() noexcept(0);
+ void f9() noexcept(b);
+ void f10() noexcept(!b);
+
+ int i;
+ static constexpr auto b = true;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S s;
+SA(noexcept(s.f1()));
+SA(!noexcept(s.f2()));
+SA(noexcept(s.f3()));
+SA(noexcept(s.f4()));
+SA(!noexcept(s.f5()));
+SA(noexcept(s.f6()));
+SA(noexcept(s.f7()));
+SA(!noexcept(s.f8()));
+SA(noexcept(s.f9()));
+SA(!noexcept(s.f10()));
+
+struct S2 {
+ struct V {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void f3() noexcept(noexcept(fn())) { }
+ void f4() noexcept(noexcept(fnx())) { }
+ void fn();
+ void fnx() noexcept;
+ } v;
+ void fn();
+ void fnx();
+};
+
+S2 s2;
+SA(!noexcept(s2.v.f1()));
+SA(noexcept(s2.v.f2()));
+SA(!noexcept(s2.v.f3()));
+SA(noexcept(s2.v.f4()));
+
+struct S3 {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S3::f1() noexcept(noexcept(fn()))
+{
+}
+
+void
+S3::f2() noexcept(noexcept(fnx()))
+{
+}
+
+struct S4 {
+ int f1 (int p) noexcept(noexcept(p)) { return p; }
+ int f2 (int p) noexcept(noexcept(p));
+ int f3 (int p = 10) noexcept(noexcept(p));
+ int f4 () noexcept(noexcept(S4{}));
+};
+
+S4 s4;
+SA(noexcept(s4.f1(1)));
+SA(noexcept(s4.f2(1)));
+SA(noexcept(s4.f3()));
+SA(noexcept(s4.f4()));
+
+template<typename T>
+struct S5 {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ int i;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S5<int> s5;
+SA(noexcept(s5.f1()));
+SA(!noexcept(s5.f2()));
+SA(noexcept(s5.f3()));
+SA(noexcept(s5.f4()));
+SA(!noexcept(s5.f5()));
+SA(noexcept(s5.f6()));
+
+template<typename T>
+struct S6 {
+ void f1() noexcept(noexcept(x));
+ T x;
+};
+
+struct S7 {
+ template<typename U>
+ void f1 () noexcept(noexcept(U(1))) { }
+
+ template<int N>
+ void f2() noexcept(noexcept(N));
+
+ template <typename _Up>
+ void f3(_Up __p) noexcept(noexcept(__p));
+};
+
+void glob();
+void globx() noexcept;
+struct S8 {
+ void f1 () noexcept(noexcept(glob()));
+ void f2 () noexcept(noexcept(globx()));
+};
+
+S8 s8;
+SA(!noexcept(s8.f1()));
+SA(noexcept(s8.f2()));
+
+struct W {
+ constexpr operator bool();
+};
+
+template<typename T>
+struct S9 {
+ S9() noexcept(noexcept(w)) { }
+ S9 &operator=(S9 &&) noexcept(T::X);
+ W w;
+};
diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
index 0ba6145ef0c..6bccc704d49 100644
--- gcc/testsuite/g++.dg/eh/shadow1.C
+++ gcc/testsuite/g++.dg/eh/shadow1.C
@@ -18,7 +18,7 @@ struct D : private B
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
struct E : public D
{
- virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
+ virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
}; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
B* foo (D *);
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-06-14 20:54 ` Marek Polacek
@ 2019-06-21 20:47 ` Jason Merrill
2019-06-21 21:30 ` Marek Polacek
0 siblings, 1 reply; 14+ messages in thread
From: Jason Merrill @ 2019-06-21 20:47 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 6/14/19 4:54 PM, Marek Polacek wrote:
> On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
>> On 6/3/19 9:01 PM, Marek Polacek wrote:
>>
>>> I sort of ended up going down a rathole, but then I realized we don't need to
>>> delay parsing of noexcept-specifiers of member friend function declarations,
>>> because they aren't members of the class.
>>
>> Where are you getting this from? I'm definitely sympathetic to the idea
>> that noexcept-specifiers of friend functions shouldn't need to be
>> complete-class contexts, but 10.3 doesn't make that distinction that I can
>> see.
>
> When I tested my patch I noticed that none of the 3 compilers I tried handled
> this scenario, so I thought I was missing something. But if the standard
> really doesn't say that noexcept-specifiers of friend functions don't have to
> be complete-class contexts, then perhaps it needs to say so. Should I raise
> this on the reflector?
Sounds good.
>>> This was a huge relief because
>>> member friend function declarations can be redeclared, so we'd have to make
>>> sure to check if their noexcept-specifiers match. But member function decls
>>> can't be redeclared. I updated the comment to better reflect why what I'm
>>> doing there is correct, along with an assert.
>>
>> But then why do you still need this:
>>
>>> + /* We can't compare unparsed noexcept-specifiers. Save the decl
>>> + and check this again after we've parsed the noexcept-specifiers
>>> + for real. */
>>> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
>>> + {
>>> + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
>>> + return;
>>> + }
>>
>> ?
>
> Eh... I don't. The following version is with the DEFARG_DECL junk removed.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2019-06-14 Marek Polacek <polacek@redhat.com>
>
> PR c++/86476 - noexcept-specifier is a complete-class context.
> PR c++/52869
> * cp-tree.def (DEFAULT_ARG): Update commentary.
I'd still like to rename this, can you do that in a follow-up?
> @@ -25203,6 +25379,26 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> if (cp_parser_is_keyword (token, RID_NOEXCEPT))
> {
> tree expr;
> +
> + /* [class.mem]/6 says that a noexcept-specifer (within the
> + member-specification of the class) is a complete-class context of
> + a class. So, if the noexcept-specifier has the optional expression,
> + just save the tokens, and reparse this after we're done with the
> + class. */
> + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
> + /* No need to delay parsing for a number literal or true/false. */
> + && !cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
> + && !(cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)
> + && (cp_lexer_nth_token_is_keyword (parser->lexer, 3, RID_FALSE)
> + || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
> + RID_TRUE)))
Maybe do immediate parsing for any keyword, not just true/false? I
can't think of a keyword that delayed parsing would make a difference for.
I think we also need to check that token 4 is close paren, so we still
get delayed parsing for noexcept (1 + foo).
Jason
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-06-21 20:47 ` Jason Merrill
@ 2019-06-21 21:30 ` Marek Polacek
2019-06-22 0:28 ` Jason Merrill
0 siblings, 1 reply; 14+ messages in thread
From: Marek Polacek @ 2019-06-21 21:30 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Fri, Jun 21, 2019 at 04:47:46PM -0400, Jason Merrill wrote:
> On 6/14/19 4:54 PM, Marek Polacek wrote:
> > On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
> > > On 6/3/19 9:01 PM, Marek Polacek wrote:
> > >
> > > > I sort of ended up going down a rathole, but then I realized we don't need to
> > > > delay parsing of noexcept-specifiers of member friend function declarations,
> > > > because they aren't members of the class.
> > >
> > > Where are you getting this from? I'm definitely sympathetic to the idea
> > > that noexcept-specifiers of friend functions shouldn't need to be
> > > complete-class contexts, but 10.3 doesn't make that distinction that I can
> > > see.
> >
> > When I tested my patch I noticed that none of the 3 compilers I tried handled
> > this scenario, so I thought I was missing something. But if the standard
> > really doesn't say that noexcept-specifiers of friend functions don't have to
> > be complete-class contexts, then perhaps it needs to say so. Should I raise
> > this on the reflector?
>
> Sounds good.
Will do.
> > > > This was a huge relief because
> > > > member friend function declarations can be redeclared, so we'd have to make
> > > > sure to check if their noexcept-specifiers match. But member function decls
> > > > can't be redeclared. I updated the comment to better reflect why what I'm
> > > > doing there is correct, along with an assert.
> > >
> > > But then why do you still need this:
> > >
> > > > + /* We can't compare unparsed noexcept-specifiers. Save the decl
> > > > + and check this again after we've parsed the noexcept-specifiers
> > > > + for real. */
> > > > + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
> > > > + {
> > > > + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
> > > > + return;
> > > > + }
> > >
> > > ?
> >
> > Eh... I don't. The following version is with the DEFARG_DECL junk removed.
> >
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> >
> > 2019-06-14 Marek Polacek <polacek@redhat.com>
> >
> > PR c++/86476 - noexcept-specifier is a complete-class context.
> > PR c++/52869
> > * cp-tree.def (DEFAULT_ARG): Update commentary.
>
> I'd still like to rename this, can you do that in a follow-up?
Absolutely.
> > @@ -25203,6 +25379,26 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
> > if (cp_parser_is_keyword (token, RID_NOEXCEPT))
> > {
> > tree expr;
> > +
> > + /* [class.mem]/6 says that a noexcept-specifer (within the
> > + member-specification of the class) is a complete-class context of
> > + a class. So, if the noexcept-specifier has the optional expression,
> > + just save the tokens, and reparse this after we're done with the
> > + class. */
> > + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
> > + /* No need to delay parsing for a number literal or true/false. */
> > + && !cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
> > + && !(cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)
> > + && (cp_lexer_nth_token_is_keyword (parser->lexer, 3, RID_FALSE)
> > + || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
> > + RID_TRUE)))
>
> Maybe do immediate parsing for any keyword, not just true/false? I can't
> think of a keyword that delayed parsing would make a difference for.
Probably true.
> I think we also need to check that token 4 is close paren, so we still get
> delayed parsing for noexcept (1 + foo).
Indeed, fixed, in a way that makes the whole conditional more readable.
I've renamed some tests, otherwise no changes.
Bootstrap/regtest running on x86_64-linux, ok for trunk if it passes?
2019-06-21 Marek Polacek <polacek@redhat.com>
PR c++/86476 - noexcept-specifier is a complete-class context.
PR c++/52869
* cp-tree.def (DEFAULT_ARG): Update commentary.
* cp-tree.h (UNPARSED_NOEXCEPT_SPEC_P): New macro.
(tree_default_arg): Use tree_base instead of tree_common.
(do_push_parm_decls, maybe_check_overriding_exception_spec): Declare.
* decl.c (do_push_parm_decls): New function, broken out of...
(store_parm_decls): ...here. Call it.
* except.c (nothrow_spec_p): Accept DEFAULT_ARG in the assert.
* parser.c (cp_parser_noexcept_specification_opt,
cp_parser_late_noexcept_specifier, noexcept_override_late_checks):
Forward-declare.
(unparsed_noexcepts): New macro.
(push_unparsed_function_queues): Update initializer.
(cp_parser_direct_declarator): Pass FRIEND_P to
cp_parser_exception_specification_opt.
(inject_parm_decls): New.
(pop_injected_parms): New.
(cp_parser_class_specifier_1): Implement delayed parsing of
noexcept-specifiers.
(cp_parser_save_noexcept): New.
(cp_parser_late_noexcept_specifier): New.
(noexcept_override_late_checks): New.
(cp_parser_noexcept_specification_opt): Add FRIEND_P parameter. Call
cp_parser_save_noexcept instead of the normal processing if needed.
(cp_parser_exception_specification_opt): Add FRIEND_P parameter and
pass it to cp_parser_noexcept_specification_opt.
(cp_parser_save_member_function_body): Fix comment.
(cp_parser_save_default_args): Maybe save the noexcept-specifier to
post process.
(cp_parser_transaction): Update call to
cp_parser_noexcept_specification_opt.
(cp_parser_transaction_expression): Likewise.
* parser.h (cp_unparsed_functions_entry): Add new field to carry
a noexcept-specifier.
* pt.c (dependent_type_p_r): Handle unparsed noexcept expression.
* search.c (maybe_check_overriding_exception_spec): New function, broken
out of...
(check_final_overrider): ...here. Call
maybe_check_overriding_exception_spec.
* tree.c (canonical_eh_spec): Handle UNPARSED_NOEXCEPT_SPEC_P.
(cp_tree_equal): Handle DEFAULT_ARG.
* g++.dg/cpp0x/noexcept45.C: New test.
* g++.dg/cpp0x/noexcept46.C: New test.
* g++.dg/cpp0x/noexcept47.C: New test.
* g++.dg/cpp0x/noexcept48.C: New test.
* g++.dg/cpp0x/noexcept49.C: New test.
* g++.dg/cpp0x/noexcept50.C: New test.
* g++.dg/cpp0x/noexcept51.C: New test.
* g++.dg/cpp0x/noexcept52.C: New test.
* g++.dg/cpp0x/noexcept53.C: New test.
* g++.dg/eh/shadow1.C: Adjust dg-error.
diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 03c105b5c4c..475c584fd4c 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -209,7 +209,8 @@ DEFTREECODE (USING_STMT, "using_stmt", tcc_statement, 1)
/* An un-parsed default argument. Holds a vector of input tokens and
a vector of places where the argument was instantiated before
- parsing had occurred. */
+ parsing had occurred. This is also used for delayed NSDMIs and
+ noexcept-specifier parsing. */
DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
/* An uninstantiated/unevaluated noexcept-specification. For the
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 98f7a0c0cd0..2c05e638915 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -1190,7 +1190,7 @@ enum cp_identifier_kind {
(((struct tree_default_arg *)DEFAULT_ARG_CHECK (NODE))->instantiations)
struct GTY (()) tree_default_arg {
- struct tree_common common;
+ struct tree_base base;
struct cp_token_cache *tokens;
vec<tree, va_gc> *instantiations;
};
@@ -1206,6 +1206,9 @@ struct GTY (()) tree_default_arg {
#define UNEVALUATED_NOEXCEPT_SPEC_P(NODE) \
(DEFERRED_NOEXCEPT_SPEC_P (NODE) \
&& DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (NODE)) == NULL_TREE)
+#define UNPARSED_NOEXCEPT_SPEC_P(NODE) \
+ ((NODE) && (TREE_PURPOSE (NODE)) \
+ && (TREE_CODE (TREE_PURPOSE (NODE)) == DEFAULT_ARG))
struct GTY (()) tree_deferred_noexcept {
struct tree_base base;
@@ -6467,6 +6470,7 @@ extern bool check_array_designated_initializer (constructor_elt *,
unsigned HOST_WIDE_INT);
extern bool check_for_uninitialized_const_var (tree, bool, tsubst_flags_t);
extern tree build_explicit_specifier (tree, tsubst_flags_t);
+extern void do_push_parm_decls (tree, tree, tree *);
/* in decl2.c */
extern void record_mangling (tree, bool);
@@ -6932,6 +6936,7 @@ extern tree copied_binfo (tree, tree);
extern tree original_binfo (tree, tree);
extern int shared_member_p (tree);
extern bool any_dependent_bases_p (tree = current_nonlambda_class_type ());
+extern bool maybe_check_overriding_exception_spec (tree, tree);
/* The representation of a deferred access check. */
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 98b54d542a0..b7ad6dead6b 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -15755,6 +15755,39 @@ use_eh_spec_block (tree fn)
&& !DECL_DEFAULTED_FN (fn));
}
+/* Helper function to push ARGS into the current lexical scope. DECL
+ is the function declaration. NONPARMS is used to handle enum
+ constants. */
+
+void
+do_push_parm_decls (tree decl, tree args, tree *nonparms)
+{
+ /* If we're doing semantic analysis, then we'll call pushdecl
+ for each of these. We must do them in reverse order so that
+ they end in the correct forward order. */
+ args = nreverse (args);
+
+ tree next;
+ for (tree parm = args; parm; parm = next)
+ {
+ next = DECL_CHAIN (parm);
+ if (TREE_CODE (parm) == PARM_DECL)
+ pushdecl (parm);
+ else if (nonparms)
+ {
+ /* If we find an enum constant or a type tag, put it aside for
+ the moment. */
+ TREE_CHAIN (parm) = NULL_TREE;
+ *nonparms = chainon (*nonparms, parm);
+ }
+ }
+
+ /* Get the decls in their original chain order and record in the
+ function. This is all and only the PARM_DECLs that were
+ pushed into scope by the loop above. */
+ DECL_ARGUMENTS (decl) = get_local_decls ();
+}
+
/* Store the parameter declarations into the current function declaration.
This is called after parsing the parameter declarations, before
digesting the body of the function.
@@ -15765,7 +15798,6 @@ static void
store_parm_decls (tree current_function_parms)
{
tree fndecl = current_function_decl;
- tree parm;
/* This is a chain of any other decls that came in among the parm
declarations. If a parm is declared with enum {foo, bar} x;
@@ -15780,35 +15812,12 @@ store_parm_decls (tree current_function_parms)
and complain if any redundant old-style parm decls were written. */
tree specparms = current_function_parms;
- tree next;
/* Must clear this because it might contain TYPE_DECLs declared
at class level. */
current_binding_level->names = NULL;
- /* If we're doing semantic analysis, then we'll call pushdecl
- for each of these. We must do them in reverse order so that
- they end in the correct forward order. */
- specparms = nreverse (specparms);
-
- for (parm = specparms; parm; parm = next)
- {
- next = DECL_CHAIN (parm);
- if (TREE_CODE (parm) == PARM_DECL)
- pushdecl (parm);
- else
- {
- /* If we find an enum constant or a type tag,
- put it aside for the moment. */
- TREE_CHAIN (parm) = NULL_TREE;
- nonparms = chainon (nonparms, parm);
- }
- }
-
- /* Get the decls in their original chain order and record in the
- function. This is all and only the PARM_DECLs that were
- pushed into scope by the loop above. */
- DECL_ARGUMENTS (fndecl) = get_local_decls ();
+ do_push_parm_decls (fndecl, specparms, &nonparms);
}
else
DECL_ARGUMENTS (fndecl) = NULL_TREE;
diff --git gcc/cp/except.c gcc/cp/except.c
index 71f5d609f10..1f87c5ab695 100644
--- gcc/cp/except.c
+++ gcc/cp/except.c
@@ -1245,6 +1245,7 @@ nothrow_spec_p (const_tree spec)
|| TREE_VALUE (spec)
|| spec == noexcept_false_spec
|| TREE_PURPOSE (spec) == error_mark_node
+ || UNPARSED_NOEXCEPT_SPEC_P (spec)
|| processing_template_decl);
return false;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 5cbc4551d1a..4d4d32973d9 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -247,6 +247,12 @@ static void cp_lexer_stop_debugging
static cp_token_cache *cp_token_cache_new
(cp_token *, cp_token *);
+static tree cp_parser_noexcept_specification_opt
+ (cp_parser *, bool, bool *, bool, bool);
+static tree cp_parser_late_noexcept_specifier
+ (cp_parser *, tree);
+static void noexcept_override_late_checks
+ (tree, tree);
static void cp_parser_initial_pragma
(cp_token *);
@@ -1974,11 +1980,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_classes \
parser->unparsed_queues->last ().classes
+#define unparsed_noexcepts \
+ parser->unparsed_queues->last ().noexcepts
static void
push_unparsed_function_queues (cp_parser *parser)
{
- cp_unparsed_functions_entry e = {NULL, make_tree_vector (), NULL, NULL};
+ cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL,
+ NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -2361,7 +2370,7 @@ static tree cp_parser_exception_declaration
static tree cp_parser_throw_expression
(cp_parser *);
static tree cp_parser_exception_specification_opt
- (cp_parser *);
+ (cp_parser *, bool = false);
static tree cp_parser_type_id_list
(cp_parser *);
@@ -20816,7 +20825,7 @@ cp_parser_direct_declarator (cp_parser* parser,
tree tx_qual = cp_parser_tx_qualifier_opt (parser);
/* And the exception-specification. */
exception_specification
- = cp_parser_exception_specification_opt (parser);
+ = cp_parser_exception_specification_opt (parser, friend_p);
attrs = cp_parser_std_attribute_spec_seq (parser);
@@ -23310,6 +23319,34 @@ cp_parser_class_name (cp_parser *parser,
return decl;
}
+/* Make sure that any member-function parameters are in scope.
+ For instance, a function's noexcept-specifier can use the function's
+ parameters:
+
+ struct S {
+ void fn (int p) noexcept(noexcept(p));
+ };
+
+ so we need to make sure name lookup can find them. This is used
+ when we delay parsing of the noexcept-specifier. */
+
+static void
+inject_parm_decls (tree decl)
+{
+ begin_scope (sk_function_parms, decl);
+ tree args = DECL_ARGUMENTS (decl);
+
+ do_push_parm_decls (decl, args, /*nonparms=*/NULL);
+}
+
+/* Undo the effects of inject_parm_decls. */
+
+static void
+pop_injected_parms (void)
+{
+ pop_bindings_and_leave_scope ();
+}
+
/* Parse a class-specifier.
class-specifier:
@@ -23620,6 +23657,60 @@ cp_parser_class_specifier_1 (cp_parser* parser)
vec_safe_truncate (unparsed_classes, 0);
after_nsdmi_defaulted_late_checks (type);
+ /* If there are noexcept-specifiers that have not yet been processed,
+ take care of them now. */
+ class_type = NULL_TREE;
+ pushed_scope = NULL_TREE;
+ FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+ {
+ tree ctx = DECL_CONTEXT (decl);
+ if (class_type != ctx)
+ {
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+ class_type = ctx;
+ pushed_scope = push_scope (class_type);
+ }
+
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ spec = TREE_PURPOSE (spec);
+
+ /* Make sure that any template parameters are in scope. */
+ maybe_begin_member_template_processing (decl);
+
+ /* Make sure that any member-function parameters are in scope. */
+ inject_parm_decls (decl);
+
+ /* 'this' is not allowed in static member functions. */
+ unsigned char local_variables_forbidden_p
+ = parser->local_variables_forbidden_p;
+ if (DECL_THIS_STATIC (decl))
+ parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+
+ /* Now we can parse the noexcept-specifier. */
+ spec = cp_parser_late_noexcept_specifier (parser, spec);
+
+ if (spec != error_mark_node)
+ TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl), spec);
+
+ /* Restore the state of local_variables_forbidden_p. */
+ parser->local_variables_forbidden_p = local_variables_forbidden_p;
+
+ /* The finish_struct call above performed various override checking,
+ but it skipped unparsed noexcept-specifier operands. Now that we
+ have resolved them, check again. */
+ noexcept_override_late_checks (type, decl);
+
+ /* Remove any member-function parameters from the symbol table. */
+ pop_injected_parms ();
+
+ /* Remove any template parameters from the symbol table. */
+ maybe_end_member_template_processing ();
+ }
+ vec_safe_truncate (unparsed_noexcepts, 0);
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+
/* Now parse the body of the functions. */
if (flag_openmp)
{
@@ -25175,6 +25266,89 @@ cp_parser_base_specifier (cp_parser* parser)
/* Exception handling [gram.exception] */
+/* Save the tokens that make up the noexcept-specifier for a member-function.
+ Returns a DEFAULT_ARG. */
+
+static tree
+cp_parser_save_noexcept (cp_parser *parser)
+{
+ cp_token *first = parser->lexer->next_token;
+ /* We want everything up to, including, the final ')'. */
+ cp_parser_cache_group (parser, CPP_CLOSE_PAREN, /*depth=*/0);
+ cp_token *last = parser->lexer->next_token;
+
+ /* As with default arguments and NSDMIs, make use of DEFAULT_ARG
+ to carry the information we will need. */
+ tree expr = make_node (DEFAULT_ARG);
+ /* Save away the noexcept-specifier; we will process it when the
+ class is complete. */
+ DEFARG_TOKENS (expr) = cp_token_cache_new (first, last);
+ expr = build_tree_list (expr, NULL_TREE);
+ return expr;
+}
+
+/* Used for late processing of noexcept-specifiers of member-functions.
+ DEFAULT_ARG is the unparsed operand of a noexcept-specifier which
+ we saved for later; parse it now. */
+
+static tree
+cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
+{
+ /* Make sure we've gotten something that hasn't been parsed yet. */
+ gcc_assert (TREE_CODE (default_arg) == DEFAULT_ARG);
+
+ push_unparsed_function_queues (parser);
+
+ /* Push the saved tokens for the noexcept-specifier onto the parser's
+ lexer stack. */
+ cp_token_cache *tokens = DEFARG_TOKENS (default_arg);
+ cp_parser_push_lexer_for_tokens (parser, tokens);
+
+ /* Parse the cached noexcept-specifier. */
+ tree parsed_arg
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/false,
+ /*friend_p=*/false);
+
+ /* Revert to the main lexer. */
+ cp_parser_pop_lexer (parser);
+
+ /* Restore the queue. */
+ pop_unparsed_function_queues (parser);
+
+ /* And we're done. */
+ return parsed_arg;
+}
+
+/* Perform late checking of overriding function with respect to their
+ noexcept-specifiers. TYPE is the class and FNDECL is the function
+ that potentially overrides some virtual function with the same
+ signature. */
+
+static void
+noexcept_override_late_checks (tree type, tree fndecl)
+{
+ tree binfo = TYPE_BINFO (type);
+ tree base_binfo;
+
+ if (DECL_STATIC_FUNCTION_P (fndecl))
+ return;
+
+ for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+ {
+ tree basetype = BINFO_TYPE (base_binfo);
+
+ if (!TYPE_POLYMORPHIC_P (basetype))
+ continue;
+
+ tree fn = look_for_overrides_here (basetype, fndecl);
+ if (fn)
+ maybe_check_overriding_exception_spec (fndecl, fn);
+ }
+}
+
/* Parse an (optional) noexcept-specification.
noexcept-specification:
@@ -25185,13 +25359,15 @@ cp_parser_base_specifier (cp_parser* parser)
expression if parentheses follow noexcept, or return BOOLEAN_TRUE_NODE if
there are no parentheses. CONSUMED_EXPR will be set accordingly.
Otherwise, returns a noexcept specification unless RETURN_COND is true,
- in which case a boolean condition is returned instead. */
+ in which case a boolean condition is returned instead. If FRIEND_P is true,
+ the function with this noexcept-specification had the `friend' specifier. */
static tree
cp_parser_noexcept_specification_opt (cp_parser* parser,
bool require_constexpr,
bool* consumed_expr,
- bool return_cond)
+ bool return_cond,
+ bool friend_p)
{
cp_token *token;
const char *saved_message;
@@ -25203,6 +25379,27 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
if (cp_parser_is_keyword (token, RID_NOEXCEPT))
{
tree expr;
+
+ /* [class.mem]/6 says that a noexcept-specifer (within the
+ member-specification of the class) is a complete-class context of
+ a class. So, if the noexcept-specifier has the optional expression,
+ just save the tokens, and reparse this after we're done with the
+ class. */
+ const bool literal_p
+ = ((cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
+ || cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD))
+ && cp_lexer_nth_token_is (parser->lexer, 4, CPP_CLOSE_PAREN));
+
+ if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
+ /* No need to delay parsing for a number literal or true/false. */
+ && !literal_p
+ && at_class_scope_p ()
+ /* Don't delay parsing for friend member functions. */
+ && !friend_p
+ && TYPE_BEING_DEFINED (current_class_type)
+ && !LAMBDA_TYPE_P (current_class_type))
+ return cp_parser_save_noexcept (parser);
+
cp_lexer_consume_token (parser->lexer);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
@@ -25273,10 +25470,11 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
throw ( type-id-list [opt] )
Returns a TREE_LIST representing the exception-specification. The
- TREE_VALUE of each node is a type. */
+ TREE_VALUE of each node is a type. If FRIEND_P is true, the function
+ with this noexcept-specification had the `friend' specifier. */
static tree
-cp_parser_exception_specification_opt (cp_parser* parser)
+cp_parser_exception_specification_opt (cp_parser* parser, bool friend_p)
{
cp_token *token;
tree type_id_list;
@@ -25286,8 +25484,12 @@ cp_parser_exception_specification_opt (cp_parser* parser)
token = cp_lexer_peek_token (parser->lexer);
/* Is it a noexcept-specification? */
- type_id_list = cp_parser_noexcept_specification_opt (parser, true, NULL,
- false);
+ type_id_list
+ = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/false,
+ friend_p);
if (type_id_list != NULL_TREE)
return type_id_list;
@@ -28435,7 +28637,7 @@ cp_parser_save_member_function_body (cp_parser* parser,
return error_mark_node;
}
- /* Remember it, if there default args to post process. */
+ /* Remember it, if there are default args to post process. */
cp_parser_save_default_args (parser, fn);
/* Save away the tokens that make up the body of the
@@ -28728,6 +28930,11 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
vec_safe_push (unparsed_funs_with_default_args, entry);
break;
}
+
+ /* Remember if there is a noexcept-specifier to post process. */
+ tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
+ if (UNPARSED_NOEXCEPT_SPEC_P (spec))
+ vec_safe_push (unparsed_noexcepts, decl);
}
/* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
@@ -40599,7 +40806,11 @@ cp_parser_transaction (cp_parser *parser, cp_token *token)
noex = NULL_TREE;
}
else
- noex = cp_parser_noexcept_specification_opt (parser, true, NULL, true);
+ noex = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/true,
+ /*consumed_expr=*/NULL,
+ /*return_cond=*/true,
+ /*friend_p=*/false);
/* Keep track if we're in the lexical scope of an outer transaction. */
new_in = this_in | (old_in & TM_STMT_ATTR_OUTER);
@@ -40659,8 +40870,11 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
parser->in_transaction = this_in;
/* Parse a noexcept specification. */
- noex = cp_parser_noexcept_specification_opt (parser, false, &noex_expr,
- true);
+ noex = cp_parser_noexcept_specification_opt (parser,
+ /*require_constexpr=*/false,
+ &noex_expr,
+ /*return_cond=*/true,
+ /*friend_p=*/false);
if (!noex || !noex_expr
|| cp_lexer_peek_token (parser->lexer)->type == CPP_OPEN_PAREN)
diff --git gcc/cp/parser.h gcc/cp/parser.h
index c03a9d87af5..2890788f489 100644
--- gcc/cp/parser.h
+++ gcc/cp/parser.h
@@ -166,6 +166,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Nested classes go in this vector, so that we can do some final
processing after parsing any NSDMIs. */
vec<tree, va_gc> *classes;
+
+ /* Functions with noexcept-specifiers that require post-processing. */
+ vec<tree, va_gc> *noexcepts;
};
diff --git gcc/cp/pt.c gcc/cp/pt.c
index 69de55369dd..fb89b933524 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -25313,8 +25313,9 @@ dependent_type_p_r (tree type)
if (tree noex = TREE_PURPOSE (spec))
/* Treat DEFERRED_NOEXCEPT as non-dependent, since it doesn't
affect overload resolution and treating it as dependent breaks
- things. */
+ things. Same for an unparsed noexcept expression. */
if (TREE_CODE (noex) != DEFERRED_NOEXCEPT
+ && TREE_CODE (noex) != DEFAULT_ARG
&& value_dependent_expression_p (noex))
return true;
return false;
diff --git gcc/cp/search.c gcc/cp/search.c
index dac08d44d76..372c4424747 100644
--- gcc/cp/search.c
+++ gcc/cp/search.c
@@ -1860,6 +1860,39 @@ locate_field_accessor (tree basetype_path, tree field_decl, bool const_p)
NULL, &lfd);
}
+/* Check throw specifier of OVERRIDER is at least as strict as
+ the one of BASEFN. */
+
+bool
+maybe_check_overriding_exception_spec (tree overrider, tree basefn)
+{
+ maybe_instantiate_noexcept (basefn);
+ maybe_instantiate_noexcept (overrider);
+ tree base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
+ tree over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
+
+ if (DECL_INVALID_OVERRIDER_P (overrider))
+ return true;
+
+ /* Can't check this yet. Pretend this is fine and let
+ noexcept_override_late_checks check this later. */
+ if (UNPARSED_NOEXCEPT_SPEC_P (base_throw)
+ || UNPARSED_NOEXCEPT_SPEC_P (over_throw))
+ return true;
+
+ if (!comp_except_specs (base_throw, over_throw, ce_derived))
+ {
+ auto_diagnostic_group d;
+ error ("looser exception specification on overriding virtual function "
+ "%q+#F", overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %q#F", basefn);
+ DECL_INVALID_OVERRIDER_P (overrider) = 1;
+ return false;
+ }
+ return true;
+}
+
/* Check that virtual overrider OVERRIDER is acceptable for base function
BASEFN. Issue diagnostic, and return zero, if unacceptable. */
@@ -1870,7 +1903,6 @@ check_final_overrider (tree overrider, tree basefn)
tree base_type = TREE_TYPE (basefn);
tree over_return = fndecl_declared_return_type (overrider);
tree base_return = fndecl_declared_return_type (basefn);
- tree over_throw, base_throw;
int fail = 0;
@@ -1954,21 +1986,8 @@ check_final_overrider (tree overrider, tree basefn)
return 0;
}
- /* Check throw specifier is at least as strict. */
- maybe_instantiate_noexcept (basefn);
- maybe_instantiate_noexcept (overrider);
- base_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (basefn));
- over_throw = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (overrider));
-
- if (!comp_except_specs (base_throw, over_throw, ce_derived))
- {
- auto_diagnostic_group d;
- error ("looser throw specifier for %q+#F", overrider);
- inform (DECL_SOURCE_LOCATION (basefn),
- "overridden function is %q#F", basefn);
- DECL_INVALID_OVERRIDER_P (overrider) = 1;
- return 0;
- }
+ if (!maybe_check_overriding_exception_spec (overrider, basefn))
+ return 0;
/* Check for conflicting type attributes. But leave transaction_safe for
set_one_vmethod_tm_attributes. */
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 978aea56193..ebfe362595f 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -2546,6 +2546,7 @@ canonical_eh_spec (tree raises)
if (raises == NULL_TREE)
return raises;
else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+ || UNPARSED_NOEXCEPT_SPEC_P (raises)
|| uses_template_parms (raises)
|| uses_template_parms (TREE_PURPOSE (raises)))
/* Keep a dependent or deferred exception specification. */
@@ -3656,6 +3657,7 @@ cp_tree_equal (tree t1, tree t2)
case IDENTIFIER_NODE:
case SSA_NAME:
case USING_DECL:
+ case DEFAULT_ARG:
return false;
case BASELINK:
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept45.C gcc/testsuite/g++.dg/cpp0x/noexcept45.C
new file mode 100644
index 00000000000..39df4a6571e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept45.C
@@ -0,0 +1,23 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+ virtual void f();
+ virtual void g() noexcept;
+ virtual void h() noexcept(false);
+};
+
+struct B : A
+{
+ void f() noexcept(true);
+ void g() noexcept(true);
+ void h() noexcept(true);
+};
+
+struct D : A
+{
+ void f() noexcept(false);
+ void g() noexcept(false); // { dg-error "looser exception specification" }
+ void h() noexcept(false);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept46.C gcc/testsuite/g++.dg/cpp0x/noexcept46.C
new file mode 100644
index 00000000000..da7490d651c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept46.C
@@ -0,0 +1,28 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void f() noexcept(false);
+void g() noexcept(true);
+void h() noexcept;
+
+struct B {
+ friend void f() noexcept(false);
+ friend void g() noexcept(false); // { dg-error "different exception specifier" }
+ friend void h() noexcept(false); // { dg-error "different exception specifier" }
+};
+
+struct C {
+ friend void f() noexcept(true); // { dg-error "different exception specifier" }
+ friend void g() noexcept(true); // { dg-error "different exception specifier" }
+ friend void h() noexcept(true); // { dg-error "different exception specifier" }
+};
+
+void o() noexcept(false);
+void p() noexcept(true);
+void q() noexcept;
+
+struct D {
+ friend void o() noexcept(true); // { dg-error "different exception specifier" }
+ friend void p() noexcept(true);
+ friend void q() noexcept(true);
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept47.C gcc/testsuite/g++.dg/cpp0x/noexcept47.C
new file mode 100644
index 00000000000..0848e68f9b1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept47.C
@@ -0,0 +1,83 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int fn1 ();
+int fn2 () noexcept;
+int fn3 () noexcept;
+
+void g() noexcept(noexcept (fn2()));
+
+struct S1 {
+ friend void g1() noexcept(noexcept(fn2()));
+ friend void g1() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S2 {
+ friend void g2() noexcept(noexcept(fn1()));
+ friend void g2() noexcept(noexcept(fn1()));
+ friend void g2() noexcept(noexcept(fn1()));
+};
+
+struct S3 {
+ friend void g3() noexcept(noexcept(fn1()));
+ friend void g3() noexcept(noexcept(fn3())); // { dg-error "different exception specifier" }
+};
+
+struct S4 {
+ friend void g4() noexcept(noexcept(fn2()));
+ friend void g4() noexcept(noexcept(fn3()));
+};
+
+struct S5 {
+ friend void g() noexcept(noexcept(fn3()));
+};
+
+struct S6 {
+ friend void g() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+};
+
+struct S7 {
+ friend void gg() noexcept(noexcept(fn3()));
+};
+
+void gg() noexcept(noexcept(fn1())); // { dg-error "different exception specifier" }
+
+struct S8 {
+ friend void g8();
+ friend void g8() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S9 {
+ friend void g9();
+ friend void g9() noexcept(noexcept(fn1()));
+};
+
+struct S10 {
+ friend void g10() noexcept(noexcept(fn1()));
+ friend void g10();
+};
+
+struct S11 {
+ friend void g11() noexcept(noexcept(fn2()));
+ friend void g11(); // { dg-error "different exception specifier" }
+};
+
+struct S12 {
+ friend void g12() noexcept(false);
+ friend void g12() noexcept(noexcept(fn2())); // { dg-error "different exception specifier" }
+};
+
+struct S13 {
+ friend void g13() noexcept(false);
+ friend void g13() noexcept(noexcept(fn1()));
+};
+
+struct S14 {
+ friend void g14() noexcept(noexcept(fn1()));
+ friend void g14() noexcept(false);
+};
+
+struct S15 {
+ friend void g15() noexcept(noexcept(fn2()));
+ friend void g15() noexcept(false); // { dg-error "different exception specifier" }
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept48.C gcc/testsuite/g++.dg/cpp0x/noexcept48.C
new file mode 100644
index 00000000000..134212c3613
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept48.C
@@ -0,0 +1,11 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+int g;
+
+struct S {
+ int b;
+ friend void fn1(int n) noexcept(noexcept(n));
+ friend void fn2() noexcept(noexcept(g));
+ friend void fn3() noexcept(noexcept(b));
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept49.C gcc/testsuite/g++.dg/cpp0x/noexcept49.C
new file mode 100644
index 00000000000..6da7ff3361f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept49.C
@@ -0,0 +1,12 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ static void f1() noexcept(b);
+ static constexpr auto b = true;
+};
+
+S s;
+SA(noexcept(s.f1()));
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept50.C gcc/testsuite/g++.dg/cpp0x/noexcept50.C
new file mode 100644
index 00000000000..43b38c2446f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept50.C
@@ -0,0 +1,147 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+#define SA(X) static_assert(X, #X)
+
+struct S {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ void f7() noexcept(1);
+ void f8() noexcept(0);
+ void f9() noexcept(b);
+ void f10() noexcept(!b);
+
+ int i;
+ static constexpr auto b = true;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S s;
+SA(noexcept(s.f1()));
+SA(!noexcept(s.f2()));
+SA(noexcept(s.f3()));
+SA(noexcept(s.f4()));
+SA(!noexcept(s.f5()));
+SA(noexcept(s.f6()));
+SA(noexcept(s.f7()));
+SA(!noexcept(s.f8()));
+SA(noexcept(s.f9()));
+SA(!noexcept(s.f10()));
+
+struct S2 {
+ struct V {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void f3() noexcept(noexcept(fn())) { }
+ void f4() noexcept(noexcept(fnx())) { }
+ void fn();
+ void fnx() noexcept;
+ } v;
+ void fn();
+ void fnx();
+};
+
+S2 s2;
+SA(!noexcept(s2.v.f1()));
+SA(noexcept(s2.v.f2()));
+SA(!noexcept(s2.v.f3()));
+SA(noexcept(s2.v.f4()));
+
+struct S3 {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S3::f1() noexcept(noexcept(fn()))
+{
+}
+
+void
+S3::f2() noexcept(noexcept(fnx()))
+{
+}
+
+struct S4 {
+ int f1 (int p) noexcept(noexcept(p)) { return p; }
+ int f2 (int p) noexcept(noexcept(p));
+ int f3 (int p = 10) noexcept(noexcept(p));
+ int f4 () noexcept(noexcept(S4{}));
+};
+
+S4 s4;
+SA(noexcept(s4.f1(1)));
+SA(noexcept(s4.f2(1)));
+SA(noexcept(s4.f3()));
+SA(noexcept(s4.f4()));
+
+template<typename T>
+struct S5 {
+ void f1() noexcept(noexcept(i)) { }
+ void f2() noexcept(noexcept(fn())) { }
+ void f3() noexcept(noexcept(fnx())) { }
+ void f4() noexcept(noexcept(i));
+ void f5() noexcept(noexcept(fn()));
+ void f6() noexcept(noexcept(fnx()));
+
+ int i;
+ void fny() noexcept(noexcept(fn()));
+ void fn();
+ void fnx() noexcept;
+};
+
+S5<int> s5;
+SA(noexcept(s5.f1()));
+SA(!noexcept(s5.f2()));
+SA(noexcept(s5.f3()));
+SA(noexcept(s5.f4()));
+SA(!noexcept(s5.f5()));
+SA(noexcept(s5.f6()));
+
+template<typename T>
+struct S6 {
+ void f1() noexcept(noexcept(x));
+ T x;
+};
+
+struct S7 {
+ template<typename U>
+ void f1 () noexcept(noexcept(U(1))) { }
+
+ template<int N>
+ void f2() noexcept(noexcept(N));
+
+ template <typename _Up>
+ void f3(_Up __p) noexcept(noexcept(__p));
+};
+
+void glob();
+void globx() noexcept;
+struct S8 {
+ void f1 () noexcept(noexcept(glob()));
+ void f2 () noexcept(noexcept(globx()));
+};
+
+S8 s8;
+SA(!noexcept(s8.f1()));
+SA(noexcept(s8.f2()));
+
+struct W {
+ constexpr operator bool();
+};
+
+template<typename T>
+struct S9 {
+ S9() noexcept(noexcept(w)) { }
+ S9 &operator=(S9 &&) noexcept(T::X);
+ W w;
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept51.C gcc/testsuite/g++.dg/cpp0x/noexcept51.C
new file mode 100644
index 00000000000..a81032f28e9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept51.C
@@ -0,0 +1,14 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+void fn1(void());
+template <typename> class A {
+ void _M_local_data();
+ A() noexcept(_M_local_data);
+};
+
+class B {
+ void _S_initialize();
+ static void _S_initialize_once();
+};
+void B::_S_initialize() { fn1(_S_initialize_once); }
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept52.C gcc/testsuite/g++.dg/cpp0x/noexcept52.C
new file mode 100644
index 00000000000..12c6d364099
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept52.C
@@ -0,0 +1,9 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+template <typename _Alloc> class A {
+ typedef _Alloc _Alloc_traits;
+ A &operator=(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ A &m_fn1(A &&) noexcept(_Alloc_traits::_S_nothrow_move);
+ void m_fn2(A<char>) {}
+};
diff --git gcc/testsuite/g++.dg/cpp0x/noexcept53.C gcc/testsuite/g++.dg/cpp0x/noexcept53.C
new file mode 100644
index 00000000000..b3859de9ebc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/noexcept53.C
@@ -0,0 +1,26 @@
+// PR c++/86476 - noexcept-specifier is a complete-class context
+// { dg-do compile { target c++11 } }
+
+struct S {
+ void f1() noexcept(noexcept(fn()));
+ void f2() noexcept(noexcept(fnx()));
+ void fn();
+ void fnx() noexcept;
+};
+
+void
+S::f1() noexcept // { dg-error "different exception specifier" }
+{
+}
+
+void
+S::f2() // { dg-error "different exception specifier" }
+{
+}
+
+struct S2 {
+ void f1() noexcept(noexcept(nosuchfn())); // { dg-error "not declared in this scope" }
+ void f2() noexcept(noexcept(nothere)); // { dg-error "not declared in this scope" }
+ void f3() noexcept(noexcept(nosuchfn())) { } // { dg-error "not declared in this scope" }
+ void f4() noexcept(noexcept(nothere)) { } // { dg-error "not declared in this scope" }
+};
diff --git gcc/testsuite/g++.dg/eh/shadow1.C gcc/testsuite/g++.dg/eh/shadow1.C
index 0ba6145ef0c..6bccc704d49 100644
--- gcc/testsuite/g++.dg/eh/shadow1.C
+++ gcc/testsuite/g++.dg/eh/shadow1.C
@@ -18,7 +18,7 @@ struct D : private B
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
struct E : public D
{
- virtual void V () throw (D); // { dg-error "looser throw" "" { target { ! c++17 } } }
+ virtual void V () throw (D); // { dg-error "looser exception" "" { target { ! c++17 } } }
}; // { dg-error "dynamic exception specification" "" { target c++17 } .-1 }
// { dg-warning "deprecated" "" { target { c++11 && { ! c++17 } } } .-2 }
B* foo (D *);
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869)
2019-06-21 21:30 ` Marek Polacek
@ 2019-06-22 0:28 ` Jason Merrill
0 siblings, 0 replies; 14+ messages in thread
From: Jason Merrill @ 2019-06-22 0:28 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 6/21/19 5:29 PM, Marek Polacek wrote:
> On Fri, Jun 21, 2019 at 04:47:46PM -0400, Jason Merrill wrote:
>> On 6/14/19 4:54 PM, Marek Polacek wrote:
>>> On Tue, Jun 11, 2019 at 11:46:05PM -0400, Jason Merrill wrote:
>>>> On 6/3/19 9:01 PM, Marek Polacek wrote:
>>>>
>>>>> I sort of ended up going down a rathole, but then I realized we don't need to
>>>>> delay parsing of noexcept-specifiers of member friend function declarations,
>>>>> because they aren't members of the class.
>>>>
>>>> Where are you getting this from? I'm definitely sympathetic to the idea
>>>> that noexcept-specifiers of friend functions shouldn't need to be
>>>> complete-class contexts, but 10.3 doesn't make that distinction that I can
>>>> see.
>>>
>>> When I tested my patch I noticed that none of the 3 compilers I tried handled
>>> this scenario, so I thought I was missing something. But if the standard
>>> really doesn't say that noexcept-specifiers of friend functions don't have to
>>> be complete-class contexts, then perhaps it needs to say so. Should I raise
>>> this on the reflector?
>>
>> Sounds good.
>
> Will do.
>
>>>>> This was a huge relief because
>>>>> member friend function declarations can be redeclared, so we'd have to make
>>>>> sure to check if their noexcept-specifiers match. But member function decls
>>>>> can't be redeclared. I updated the comment to better reflect why what I'm
>>>>> doing there is correct, along with an assert.
>>>>
>>>> But then why do you still need this:
>>>>
>>>>> + /* We can't compare unparsed noexcept-specifiers. Save the decl
>>>>> + and check this again after we've parsed the noexcept-specifiers
>>>>> + for real. */
>>>>> + if (UNPARSED_NOEXCEPT_SPEC_P (new_exceptions))
>>>>> + {
>>>>> + DEFARG_DECL (TREE_PURPOSE (new_exceptions)) = copy_decl (old_decl);
>>>>> + return;
>>>>> + }
>>>>
>>>> ?
>>>
>>> Eh... I don't. The following version is with the DEFARG_DECL junk removed.
>>>
>>> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>>>
>>> 2019-06-14 Marek Polacek <polacek@redhat.com>
>>>
>>> PR c++/86476 - noexcept-specifier is a complete-class context.
>>> PR c++/52869
>>> * cp-tree.def (DEFAULT_ARG): Update commentary.
>>
>> I'd still like to rename this, can you do that in a follow-up?
>
> Absolutely.
>
>>> @@ -25203,6 +25379,26 @@ cp_parser_noexcept_specification_opt (cp_parser* parser,
>>> if (cp_parser_is_keyword (token, RID_NOEXCEPT))
>>> {
>>> tree expr;
>>> +
>>> + /* [class.mem]/6 says that a noexcept-specifer (within the
>>> + member-specification of the class) is a complete-class context of
>>> + a class. So, if the noexcept-specifier has the optional expression,
>>> + just save the tokens, and reparse this after we're done with the
>>> + class. */
>>> + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_PAREN)
>>> + /* No need to delay parsing for a number literal or true/false. */
>>> + && !cp_lexer_nth_token_is (parser->lexer, 3, CPP_NUMBER)
>>> + && !(cp_lexer_nth_token_is (parser->lexer, 3, CPP_KEYWORD)
>>> + && (cp_lexer_nth_token_is_keyword (parser->lexer, 3, RID_FALSE)
>>> + || cp_lexer_nth_token_is_keyword (parser->lexer, 3,
>>> + RID_TRUE)))
>>
>> Maybe do immediate parsing for any keyword, not just true/false? I can't
>> think of a keyword that delayed parsing would make a difference for.
>
> Probably true.
>
>> I think we also need to check that token 4 is close paren, so we still get
>> delayed parsing for noexcept (1 + foo).
>
> Indeed, fixed, in a way that makes the whole conditional more readable.
>
> I've renamed some tests, otherwise no changes.
>
> Bootstrap/regtest running on x86_64-linux, ok for trunk if it passes?
Yes, thanks.
Jason
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2019-06-22 0:28 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-19 20:27 C++ PATCH to implement deferred parsing of noexcept-specifiers (c++/86476, c++/52869) Marek Polacek
2019-01-04 14:45 ` Marek Polacek
2019-01-07 15:44 ` Jason Merrill
2019-05-10 19:21 ` Marek Polacek
2019-05-17 14:35 ` Marek Polacek
2019-05-24 16:17 ` Marek Polacek
2019-05-28 15:48 ` Jason Merrill
2019-06-04 1:02 ` Marek Polacek
2019-06-10 12:28 ` Marek Polacek
2019-06-12 3:46 ` Jason Merrill
2019-06-14 20:54 ` Marek Polacek
2019-06-21 20:47 ` Jason Merrill
2019-06-21 21:30 ` Marek Polacek
2019-06-22 0:28 ` Jason Merrill
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).