c++: ICE with late parsing of noexcept in nested class [PR98899]
Here we crash with a noexcept-specifier in a nested template class,
because my handling of such deferred-parse noexcept-specifiers was
gronked when we need to instantiate a DEFERRED_PARSE before it was
actually parsed at the end of the outermost class.
In
struct S {
template<class> struct B {
B() noexcept(noexcept(x));
int x;
};
struct A : B<int> {
A() : B() {}
};
};
we call complete_type for B<int> which triggers tsubsting S::B<int>::B()
whose noexcept-specifier still contains a DEFERRED_PARSE. The trick is
to stash such noexcept-specifiers into DEFPARSE_INSTANTIATIONS so that
we can replace it later when we've finally parsed all deferred
noexcept-specifiers.
In passing, fix missing usage of UNPARSED_NOEXCEPT_SPEC_P.
gcc/cp/ChangeLog:
PR c++/98899
* parser.c (cp_parser_class_specifier_1): Use any possible
DEFPARSE_INSTANTIATIONS to update DEFERRED_NOEXCEPT_PATTERN.
(cp_parser_save_noexcept): Initialize DEFPARSE_INSTANTIATIONS.
* pt.c (tsubst_exception_specification): Stash new_specs into
DEFPARSE_INSTANTIATIONS.
* tree.c (fixup_deferred_exception_variants): Use
UNPARSED_NOEXCEPT_SPEC_P.
gcc/testsuite/ChangeLog:
PR c++/98899
* g++.dg/cpp0x/noexcept65.C: New test.
This commit is contained in:
parent
3535402e20
commit
25fdd0d6df
@ -25026,8 +25026,8 @@ cp_parser_class_specifier_1 (cp_parser* parser)
|
||||
pushed_scope = push_scope (class_type);
|
||||
}
|
||||
|
||||
tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
|
||||
spec = TREE_PURPOSE (spec);
|
||||
tree def_parse = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
|
||||
def_parse = TREE_PURPOSE (def_parse);
|
||||
|
||||
/* Make sure that any template parameters are in scope. */
|
||||
maybe_begin_member_template_processing (decl);
|
||||
@ -25044,7 +25044,7 @@ cp_parser_class_specifier_1 (cp_parser* parser)
|
||||
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
|
||||
|
||||
/* Now we can parse the noexcept-specifier. */
|
||||
spec = cp_parser_late_noexcept_specifier (parser, spec);
|
||||
tree spec = cp_parser_late_noexcept_specifier (parser, def_parse);
|
||||
|
||||
if (spec == error_mark_node)
|
||||
spec = NULL_TREE;
|
||||
@ -25052,6 +25052,12 @@ cp_parser_class_specifier_1 (cp_parser* parser)
|
||||
/* Update the fn's type directly -- it might have escaped
|
||||
beyond this decl :( */
|
||||
fixup_deferred_exception_variants (TREE_TYPE (decl), spec);
|
||||
/* Update any instantiations we've already created. We must
|
||||
keep the new noexcept-specifier wrapped in a DEFERRED_NOEXCEPT
|
||||
so that maybe_instantiate_noexcept can tsubst the NOEXCEPT_EXPR
|
||||
in the pattern. */
|
||||
for (tree i : DEFPARSE_INSTANTIATIONS (def_parse))
|
||||
DEFERRED_NOEXCEPT_PATTERN (TREE_PURPOSE (i)) = TREE_PURPOSE (spec);
|
||||
|
||||
/* Restore the state of local_variables_forbidden_p. */
|
||||
parser->local_variables_forbidden_p = local_variables_forbidden_p;
|
||||
@ -26695,6 +26701,7 @@ cp_parser_save_noexcept (cp_parser *parser)
|
||||
/* Save away the noexcept-specifier; we will process it when the
|
||||
class is complete. */
|
||||
DEFPARSE_TOKENS (expr) = cp_token_cache_new (first, last);
|
||||
DEFPARSE_INSTANTIATIONS (expr) = nullptr;
|
||||
expr = build_tree_list (expr, NULL_TREE);
|
||||
return expr;
|
||||
}
|
||||
|
||||
16
gcc/cp/pt.c
16
gcc/cp/pt.c
@ -15189,6 +15189,22 @@ tsubst_exception_specification (tree fntype,
|
||||
/*integral_constant_expression_p=*/true);
|
||||
}
|
||||
new_specs = build_noexcept_spec (new_specs, complain);
|
||||
/* We've instantiated a template before a noexcept-specifier
|
||||
contained therein has been parsed. This can happen for
|
||||
a nested template class:
|
||||
|
||||
struct S {
|
||||
template<typename> struct B { B() noexcept(...); };
|
||||
struct A : B<int> { ... use B() ... };
|
||||
};
|
||||
|
||||
where completing B<int> will trigger instantiating the
|
||||
noexcept, even though we only parse it at the end of S. */
|
||||
if (UNPARSED_NOEXCEPT_SPEC_P (specs))
|
||||
{
|
||||
gcc_checking_assert (defer_ok);
|
||||
vec_safe_push (DEFPARSE_INSTANTIATIONS (expr), new_specs);
|
||||
}
|
||||
}
|
||||
else if (specs)
|
||||
{
|
||||
|
||||
@ -2738,8 +2738,7 @@ fixup_deferred_exception_variants (tree type, tree raises)
|
||||
tree original = TYPE_RAISES_EXCEPTIONS (type);
|
||||
tree cr = flag_noexcept_type ? canonical_eh_spec (raises) : NULL_TREE;
|
||||
|
||||
gcc_checking_assert (TREE_CODE (TREE_PURPOSE (original))
|
||||
== DEFERRED_PARSE);
|
||||
gcc_checking_assert (UNPARSED_NOEXCEPT_SPEC_P (original));
|
||||
|
||||
/* Though sucky, this walk will process the canonical variants
|
||||
first. */
|
||||
|
||||
35
gcc/testsuite/g++.dg/cpp0x/noexcept65.C
Normal file
35
gcc/testsuite/g++.dg/cpp0x/noexcept65.C
Normal file
@ -0,0 +1,35 @@
|
||||
// PR c++/98899
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
template <int __v> struct integral_constant {
|
||||
static constexpr int value = __v;
|
||||
};
|
||||
|
||||
struct S {
|
||||
template<class> struct B {
|
||||
B() noexcept(noexcept(x));
|
||||
int x;
|
||||
};
|
||||
struct A : B<int> {
|
||||
A() : B() {}
|
||||
};
|
||||
};
|
||||
|
||||
struct S2 {
|
||||
template<class> struct B {
|
||||
B() noexcept(integral_constant<false>::value);
|
||||
};
|
||||
struct A : B<int> {
|
||||
A() : B() {}
|
||||
};
|
||||
};
|
||||
|
||||
struct S3 {
|
||||
template<class> struct B {
|
||||
B() noexcept(b);
|
||||
};
|
||||
struct A : B<int> {
|
||||
A() : B() {}
|
||||
};
|
||||
static constexpr bool b = false;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user