commit 03a304001f8fdb776a96648786fe667ad416ebeb
Author: Jason Merrill <jason@redhat.com>
Date: Wed Jun 8 09:06:07 2011 -0400
PR c++/49107
* cp-tree.def (DEFERRED_NOEXCEPT): New.
* cp-tree.h (struct tree_deferred_noexcept): New.
(DEFERRED_NOEXCEPT_PATTERN, DEFERRED_NOEXCEPT_ARGS): New.
(DEFERRED_NOEXCEPT_SPEC_P): New.
(enum cp_tree_node_structure_enum): Add TS_CP_DEFERRED_NOEXCEPT.
(union lang_tree_node): Add tree_deferred_noexcept.
(maybe_instantiate_noexcept): Declare.
* cp-objcp-common.c (cp_tree_size): Handle DEFERRED_NOEXCEPT.
* error.c (dump_exception_spec): Likewise.
* cxx-pretty-print.c (pp_cxx_exception_specification): Likewise.
* ptree.c (cxx_print_xnode): Likewise.
* tree.c (cp_tree_equal): Likewise.
* decl.c (cp_tree_node_structure): Likewise.
(duplicate_decls): Call maybe_instantiate_noexcept.
* except.c (build_noexcept_spec): Handle DEFERRED_NOEXCEPT.
(nothrow_spec_p, type_noexcept_p, type_throw_all_p): Check
DEFERRED_NOEXCEPT_SPEC_P.
* typeck2.c (merge_exception_specifiers): Likewise.
* decl2.c (mark_used): Call maybe_instantiate_noexcept.
* method.c (process_subob_fn, defaulted_late_check): Likewise.
* pt.c (tsubst_exception_specification): Add defer_ok parm.
Build DEFERRED_NOEXCEPT.
(maybe_instantiate_noexcept): New.
(tsubst, regenerate_decl_from_template, instantiate_decl): Adjust.
* search.c (check_final_overrider): Call maybe_instantiate_noexcept.
@@ -79,6 +79,7 @@ cp_tree_size (enum tree_code code)
case BASELINK: return sizeof (struct tree_baselink);
case TEMPLATE_PARM_INDEX: return sizeof (template_parm_index);
case DEFAULT_ARG: return sizeof (struct tree_default_arg);
+ case DEFERRED_NOEXCEPT: return sizeof (struct tree_deferred_noexcept);
case OVERLOAD: return sizeof (struct tree_overload);
case STATIC_ASSERT: return sizeof (struct tree_static_assert);
case TYPE_ARGUMENT_PACK:
@@ -214,6 +214,11 @@ DEFTREECODE (USING_STMT, "using_directive", tcc_statement, 1)
parsing had occurred. */
DEFTREECODE (DEFAULT_ARG, "default_arg", tcc_exceptional, 0)
+/* An uninstantiated noexcept-specification. DEFERRED_NOEXCEPT_PATTERN is
+ the pattern from the template, and DEFERRED_NOEXCEPT_ARGS are the
+ template arguments to substitute into the pattern when needed. */
+DEFTREECODE (DEFERRED_NOEXCEPT, "deferred_noexcept", tcc_exceptional, 0)
+
/* A template-id, like foo<int>. The first operand is the template.
The second is NULL if there are no explicit arguments, or a
TREE_VEC of arguments. The template will be a FUNCTION_DECL,
@@ -507,6 +507,22 @@ struct GTY (()) tree_default_arg {
VEC(tree,gc) *instantiations;
};
+
+#define DEFERRED_NOEXCEPT_PATTERN(NODE) \
+ (((struct tree_deferred_noexcept *)DEFERRED_NOEXCEPT_CHECK (NODE))->pattern)
+#define DEFERRED_NOEXCEPT_ARGS(NODE) \
+ (((struct tree_deferred_noexcept *)DEFERRED_NOEXCEPT_CHECK (NODE))->args)
+#define DEFERRED_NOEXCEPT_SPEC_P(NODE) \
+ ((NODE) && (TREE_PURPOSE (NODE)) \
+ && TREE_CODE (TREE_PURPOSE (NODE)) == DEFERRED_NOEXCEPT)
+
+struct GTY (()) tree_deferred_noexcept {
+ struct tree_base base;
+ tree pattern;
+ tree args;
+};
+
+
/* The condition associated with the static assertion. This must be
an integral constant expression. */
#define STATIC_ASSERT_CONDITION(NODE) \
@@ -693,6 +709,7 @@ enum cp_tree_node_structure_enum {
TS_CP_BASELINK,
TS_CP_WRAPPER,
TS_CP_DEFAULT_ARG,
+ TS_CP_DEFERRED_NOEXCEPT,
TS_CP_STATIC_ASSERT,
TS_CP_ARGUMENT_PACK_SELECT,
TS_CP_TRAIT_EXPR,
@@ -711,6 +728,7 @@ union GTY((desc ("cp_tree_node_structure (&%h)"),
struct tree_overload GTY ((tag ("TS_CP_OVERLOAD"))) overload;
struct tree_baselink GTY ((tag ("TS_CP_BASELINK"))) baselink;
struct tree_default_arg GTY ((tag ("TS_CP_DEFAULT_ARG"))) default_arg;
+ struct tree_deferred_noexcept GTY ((tag ("TS_CP_DEFERRED_NOEXCEPT"))) deferred_noexcept;
struct lang_identifier GTY ((tag ("TS_CP_IDENTIFIER"))) identifier;
struct tree_static_assert GTY ((tag ("TS_CP_STATIC_ASSERT")))
static_assertion;
@@ -5130,6 +5148,7 @@ extern int more_specialized_fn (tree, tree, int);
extern void do_decl_instantiation (tree, tree);
extern void do_type_instantiation (tree, tree, tsubst_flags_t);
extern bool always_instantiate_p (tree);
+extern void maybe_instantiate_noexcept (tree);
extern tree instantiate_decl (tree, int, bool);
extern int comp_template_parms (const_tree, const_tree);
extern bool uses_parameter_packs (tree);
@@ -1446,7 +1446,10 @@ pp_cxx_exception_specification (cxx_pretty_printer *pp, tree t)
pp_cxx_ws_string (pp, "noexcept");
pp_cxx_whitespace (pp);
pp_cxx_left_paren (pp);
- pp_cxx_expression (pp, TREE_PURPOSE (ex_spec));
+ if (DEFERRED_NOEXCEPT_SPEC_P (ex_spec))
+ pp_cxx_ws_string (pp, "<uninstantiated>");
+ else
+ pp_cxx_expression (pp, TREE_PURPOSE (ex_spec));
pp_cxx_right_paren (pp);
return;
}
@@ -1784,6 +1784,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
tree oldtype = TREE_TYPE (olddecl);
tree newtype;
+ if (TREE_CODE (newdecl) == FUNCTION_DECL)
+ maybe_instantiate_noexcept (olddecl);
+
/* Merge the data types specified in the two decls. */
newtype = merge_types (TREE_TYPE (newdecl), TREE_TYPE (olddecl));
@@ -13665,6 +13668,7 @@ cp_tree_node_structure (union lang_tree_node * t)
switch (TREE_CODE (&t->generic))
{
case DEFAULT_ARG: return TS_CP_DEFAULT_ARG;
+ case DEFERRED_NOEXCEPT: return TS_CP_DEFERRED_NOEXCEPT;
case IDENTIFIER_NODE: return TS_CP_IDENTIFIER;
case OVERLOAD: return TS_CP_OVERLOAD;
case TEMPLATE_PARM_INDEX: return TS_CP_TPI;
@@ -4228,6 +4228,9 @@ mark_used (tree decl)
return;
}
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ maybe_instantiate_noexcept (decl);
+
/* Normally, we can wait until instantiation-time to synthesize DECL.
However, if DECL is a static data member initialized with a constant
or a constexpr function, we need it right now because a reference to
@@ -1438,7 +1438,10 @@ dump_exception_spec (tree t, int flags)
pp_cxx_ws_string (cxx_pp, "noexcept");
pp_cxx_whitespace (cxx_pp);
pp_cxx_left_paren (cxx_pp);
- dump_expr (TREE_PURPOSE (t), flags);
+ if (DEFERRED_NOEXCEPT_SPEC_P (t))
+ pp_cxx_ws_string (cxx_pp, "<uninstantiated>");
+ else
+ dump_expr (TREE_PURPOSE (t), flags);
pp_cxx_right_paren (cxx_pp);
}
else if (t)
@@ -1160,6 +1160,7 @@ finish_noexcept_expr (tree expr, tsubst_flags_t complain)
bool
nothrow_spec_p (const_tree spec)
{
+ gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec));
if (spec == NULL_TREE
|| TREE_VALUE (spec) != NULL_TREE
|| spec == noexcept_false_spec)
@@ -1180,6 +1181,7 @@ bool
type_noexcept_p (const_tree type)
{
tree spec = TYPE_RAISES_EXCEPTIONS (type);
+ gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec));
if (flag_nothrow_opt)
return nothrow_spec_p (spec);
else
@@ -1193,6 +1195,7 @@ bool
type_throw_all_p (const_tree type)
{
tree spec = TYPE_RAISES_EXCEPTIONS (type);
+ gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec));
return spec == NULL_TREE || spec == noexcept_false_spec;
}
@@ -1204,7 +1207,7 @@ build_noexcept_spec (tree expr, int complain)
{
/* This isn't part of the signature, so don't bother trying to evaluate
it until instantiation. */
- if (!processing_template_decl)
+ if (!processing_template_decl && TREE_CODE (expr) != DEFERRED_NOEXCEPT)
{
expr = perform_implicit_conversion_flags (boolean_type_node, expr,
complain,
@@ -1219,7 +1222,8 @@ build_noexcept_spec (tree expr, int complain)
return error_mark_node;
else
{
- gcc_assert (processing_template_decl || expr == error_mark_node);
+ gcc_assert (processing_template_decl || expr == error_mark_node
+ || TREE_CODE (expr) == DEFERRED_NOEXCEPT);
return build_tree_list (expr, NULL_TREE);
}
}
@@ -923,7 +923,9 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
if (spec_p)
{
- tree raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+ tree raises;
+ maybe_instantiate_noexcept (fn);
+ raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
*spec_p = merge_exception_specifiers (*spec_p, raises);
}
@@ -1558,7 +1560,9 @@ defaulted_late_check (tree fn)
it had been implicitly declared. */
if (DECL_DEFAULTED_IN_CLASS_P (fn))
{
- tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
+ tree eh_spec;
+ maybe_instantiate_noexcept (fn);
+ eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn))
&& !comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)),
eh_spec, ce_normal))
@@ -10342,7 +10342,8 @@ static tree
tsubst_exception_specification (tree fntype,
tree args,
tsubst_flags_t complain,
- tree in_decl)
+ tree in_decl,
+ bool defer_ok)
{
tree specs;
tree new_specs;
@@ -10352,9 +10353,33 @@ tsubst_exception_specification (tree fntype,
if (specs && TREE_PURPOSE (specs))
{
/* A noexcept-specifier. */
- new_specs = tsubst_copy_and_build
- (TREE_PURPOSE (specs), args, complain, in_decl, /*function_p=*/false,
- /*integral_constant_expression_p=*/true);
+ tree expr = TREE_PURPOSE (specs);
+ if (expr == boolean_true_node || expr == boolean_false_node)
+ new_specs = expr;
+ else if (defer_ok)
+ {
+ /* Defer instantiation of noexcept-specifiers to avoid
+ excessive instantiations (c++/49107). */
+ new_specs = make_node (DEFERRED_NOEXCEPT);
+ if (DEFERRED_NOEXCEPT_SPEC_P (specs))
+ {
+ /* We already partially instantiated this member template,
+ so combine the new args with the old. */
+ DEFERRED_NOEXCEPT_PATTERN (new_specs)
+ = DEFERRED_NOEXCEPT_PATTERN (expr);
+ DEFERRED_NOEXCEPT_ARGS (new_specs)
+ = add_to_template_args (DEFERRED_NOEXCEPT_ARGS (expr), args);
+ }
+ else
+ {
+ DEFERRED_NOEXCEPT_PATTERN (new_specs) = expr;
+ DEFERRED_NOEXCEPT_ARGS (new_specs) = args;
+ }
+ }
+ else
+ new_specs = tsubst_copy_and_build
+ (expr, args, complain, in_decl, /*function_p=*/false,
+ /*integral_constant_expression_p=*/true);
new_specs = build_noexcept_spec (new_specs, complain);
}
else if (specs)
@@ -10879,7 +10904,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
/* Substitute the exception specification. */
specs = tsubst_exception_specification (t, args, complain,
- in_decl);
+ in_decl, /*defer_ok*/true);
if (specs == error_mark_node)
return error_mark_node;
if (specs)
@@ -17159,7 +17184,8 @@ regenerate_decl_from_template (tree decl, tree tmpl)
args = get_innermost_template_args (args, parms_depth);
specs = tsubst_exception_specification (TREE_TYPE (code_pattern),
- args, tf_error, NULL_TREE);
+ args, tf_error, NULL_TREE,
+ /*defer_ok*/false);
if (specs)
TREE_TYPE (decl) = build_exception_variant (TREE_TYPE (decl),
specs);
@@ -17324,6 +17350,46 @@ always_instantiate_p (tree decl)
&& decl_maybe_constant_var_p (decl)));
}
+/* If FN has a noexcept-specifier that hasn't been instantiated yet,
+ instantiate it now, modifying TREE_TYPE (fn). */
+
+void
+maybe_instantiate_noexcept (tree fn)
+{
+ tree fntype = TREE_TYPE (fn);
+ tree spec = TYPE_RAISES_EXCEPTIONS (fntype);
+ tree noex = NULL_TREE;
+ location_t saved_loc = input_location;
+ tree clone;
+
+ if (!DEFERRED_NOEXCEPT_SPEC_P (spec))
+ return;
+ noex = TREE_PURPOSE (spec);
+
+ push_tinst_level (fn);
+ push_access_scope (fn);
+ input_location = DECL_SOURCE_LOCATION (fn);
+ noex = tsubst_copy_and_build (DEFERRED_NOEXCEPT_PATTERN (noex),
+ DEFERRED_NOEXCEPT_ARGS (noex),
+ tf_warning_or_error, fn, /*function_p=*/false,
+ /*integral_constant_expression_p=*/true);
+ input_location = saved_loc;
+ pop_access_scope (fn);
+ pop_tinst_level ();
+ spec = build_noexcept_spec (noex, tf_warning_or_error);
+ if (spec == error_mark_node)
+ spec = noexcept_false_spec;
+ TREE_TYPE (fn) = build_exception_variant (fntype, spec);
+
+ FOR_EACH_CLONE (clone, fn)
+ {
+ if (TREE_TYPE (clone) == fntype)
+ TREE_TYPE (clone) = TREE_TYPE (fn);
+ else
+ TREE_TYPE (clone) = build_exception_variant (TREE_TYPE (clone), spec);
+ }
+}
+
/* Produce the definition of D, a _DECL generated from a template. If
DEFER_OK is nonzero, then we don't have to actually do the
instantiation now; we just have to do it sometime. Normally it is
@@ -17460,6 +17526,9 @@ instantiate_decl (tree d, int defer_ok,
SET_DECL_IMPLICIT_INSTANTIATION (d);
}
+ if (TREE_CODE (d) == FUNCTION_DECL)
+ maybe_instantiate_noexcept (d);
+
/* Recheck the substitutions to obtain any warning messages
about ignoring cv qualifiers. Don't do this for artificial decls,
as it breaks the context-sensitive substitution for lambda op(). */
@@ -17477,7 +17546,7 @@ instantiate_decl (tree d, int defer_ok,
{
tsubst (DECL_ARGUMENTS (gen), gen_args, tf_warning_or_error, d);
tsubst_exception_specification (type, gen_args, tf_warning_or_error,
- d);
+ d, /*defer_ok*/true);
/* Don't simply tsubst the function type, as that will give
duplicate warnings about poor parameter qualifications.
The function arguments are the same as the decl_arguments
@@ -227,6 +227,10 @@ cxx_print_xnode (FILE *file, tree node, int indent)
indent_to (file, indent + 3);
fprintf (file, "index %d", ARGUMENT_PACK_SELECT_INDEX (node));
break;
+ case DEFERRED_NOEXCEPT:
+ print_node (file, "pattern", DEFERRED_NOEXCEPT_PATTERN (node), indent+4);
+ print_node (file, "args", DEFERRED_NOEXCEPT_ARGS (node), indent+4);
+ break;
default:
break;
}
@@ -1803,8 +1803,8 @@ check_final_overrider (tree overrider, tree basefn)
tree base_type = TREE_TYPE (basefn);
tree over_return = TREE_TYPE (over_type);
tree base_return = TREE_TYPE (base_type);
- tree over_throw = TYPE_RAISES_EXCEPTIONS (over_type);
- tree base_throw = TYPE_RAISES_EXCEPTIONS (base_type);
+ tree over_throw, base_throw;
+
int fail = 0;
if (DECL_INVALID_OVERRIDER_P (overrider))
@@ -1888,6 +1888,11 @@ check_final_overrider (tree overrider, tree basefn)
}
/* 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))
{
error ("looser throw specifier for %q+#F", overrider);
@@ -2340,6 +2340,13 @@ cp_tree_equal (tree t1, tree t2)
/* Now compare operands as usual. */
break;
+ case DEFERRED_NOEXCEPT:
+ return (cp_tree_equal (DEFERRED_NOEXCEPT_PATTERN (t1),
+ DEFERRED_NOEXCEPT_PATTERN (t2))
+ && comp_template_args (DEFERRED_NOEXCEPT_ARGS (t1),
+ DEFERRED_NOEXCEPT_ARGS (t2)));
+ break;
+
default:
break;
}
@@ -1769,10 +1769,15 @@ merge_exception_specifiers (tree list, tree add)
return list;
else if (!add || add == noexcept_false_spec)
return add;
+
+ /* We need to instantiate deferred noexcept before we get here. */
+ gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (list)
+ && !DEFERRED_NOEXCEPT_SPEC_P (add));
+
/* For merging noexcept(true) and throw(), take the more recent one (LIST).
Any other noexcept-spec should only be merged with an equivalent one.
So the !TREE_VALUE code below is correct for all cases. */
- else if (!TREE_VALUE (add))
+ if (!TREE_VALUE (add))
return list;
else if (!TREE_VALUE (list))
return add;
new file mode 100644
@@ -0,0 +1,53 @@
+// PR c++/49107
+// { dg-options -std=c++0x }
+
+template<typename _Tp>
+_Tp declval() noexcept;
+
+template<typename _Tp , typename = decltype(_Tp(declval<_Tp&&>()))>
+struct trait
+{
+ static const bool value=true;
+};
+
+template<class _T2>
+struct pair
+{
+ _T2 second;
+ void swap(pair& __p)
+ noexcept(trait<_T2>::value);
+};
+
+template < class R_ >
+struct Main
+{
+ Main() {}
+ Main(const typename R_::Sub1T& r) ;
+ Main(const typename R_::Sub2T& l) ;
+};
+
+template < class R_ >
+class Sub1
+{
+ typedef pair<typename R_::MainT> Rep;
+ Rep base;
+};
+
+template < class R_ >
+struct Sub2
+{
+ typedef pair<typename R_::MainT> Rep;
+ Rep base;
+};
+
+struct Kernel
+{
+ typedef Main<Kernel> MainT;
+ typedef Sub1<Kernel> Sub1T;
+ typedef Sub2<Kernel> Sub2T;
+};
+
+Main<Kernel> f()
+{
+ return Main<Kernel> ();
+}
new file mode 100644
@@ -0,0 +1,11 @@
+// Test that we handle merging with deferred noexcept.
+// { dg-options -std=c++0x }
+
+template <class U>
+struct O
+{
+ template <class T>
+ void f() noexcept(noexcept(T()));
+};
+
+template<> template<> void O<int>::f<int>() noexcept { }
@@ -6,7 +6,7 @@ template<class T>
T&& declval() noexcept;
template< class T >
-inline void f1( T& x ) noexcept( noexcept( declval<T&>().foo() ) )
+inline void f1( T& x ) noexcept( noexcept( declval<T&>().foo() ) ) // { dg-error "Z" }
{
x.foo();
}
@@ -21,7 +21,7 @@ inline void f2( T& x ) noexcept( Noexcept )
// a common and trivial mistake
template< class T >
-inline void f3( T& x ) noexcept( declval<T&>().foo() )
+inline void f3( T& x ) noexcept( declval<T&>().foo() ) // { dg-error "Z" }
{
x.foo();
}
@@ -50,7 +50,7 @@ int main()
static_assert( noexcept( f2(y) ), "OK." );
// static_assert( noexcept( f3(y) ), "shall be ill-formed(OK)." );
- static_assert( noexcept( f1(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+ noexcept( f1(z) ); // { dg-message "required" }
static_assert( noexcept( f2(z) ), "shall be ill-formed." ); // { dg-error "no match" }
- static_assert( !noexcept( f3(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+ noexcept( f3(z) ); // { dg-message "required" }
}