From 4cc1d462013c5fcaa82b0ad82b4232bc1249b31c Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Wed, 4 Aug 1999 09:07:51 +0000 Subject: [PATCH] cp-tree.h (empty_except_spec): New global var. * cp-tree.h (empty_except_spec): New global var. (compexcepttypes): Remove prototype. (comp_except_specs): Prototype new global function. (add_exception_specifier): Prototype new global function. * decl.c (empty_except_spec): Define new global var. (duplicate_decls): Use comp_except_specs, reword error message. (init_decl_processing): Initialize empty_except_spec. Adjust build_exception_variant calls. * parse.y (exception_specification_opt): Use empty_except_spec. (ansi_raise_identifier): Call check_for_new_type. (ansi_raise_identifiers): Use add_exception_specifier. * pt.c (tsubst): Use add_exception_specifier to build exception specifier. * search.c (check_final_overrider): New static function, broken out of get_matching_virtual. Check throw specifiers, reword diagnostics. (get_matching_virtual): Use check_final_overrider. * tree.c (build_exception_variant): Use comp_except_specs. * typeck.c (compexcepttypes): Remove. (comp_except_types): New static function, helper for comp_except_specs. Compare two types as exception specifiers. (comp_except_specs): New global function, compare two exception specifiers. (comptypes): Adjust for comp_except_specs. * typeck2.c (add_exception_specifier): New global function. * class.c (check_for_override): Reword error message. From-SVN: r28494 --- gcc/cp/ChangeLog | 30 ++++++++++++++ gcc/cp/class.c | 5 +-- gcc/cp/cp-tree.h | 4 +- gcc/cp/decl.c | 19 +++++---- gcc/cp/parse.y | 13 +++--- gcc/cp/pt.c | 20 +++++++-- gcc/cp/search.c | 89 +++++++++++++++++++++++++-------------- gcc/cp/tree.c | 22 ++-------- gcc/cp/typeck.c | 105 ++++++++++++++++++++++++++++++++++++++++++++--- gcc/cp/typeck2.c | 53 ++++++++++++++++++++++++ 10 files changed, 284 insertions(+), 76 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 74bde45ccf5..3617a86eb47 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,33 @@ +1999-08-04 Nathan Sidwell + + * cp-tree.h (empty_except_spec): New global var. + (compexcepttypes): Remove prototype. + (comp_except_specs): Prototype new global function. + (add_exception_specifier): Prototype new global function. + * decl.c (empty_except_spec): Define new global var. + (duplicate_decls): Use comp_except_specs, reword error message. + (init_decl_processing): Initialize empty_except_spec. + Adjust build_exception_variant calls. + * parse.y (exception_specification_opt): Use empty_except_spec. + (ansi_raise_identifier): Call check_for_new_type. + (ansi_raise_identifiers): Use add_exception_specifier. + * pt.c (tsubst): Use add_exception_specifier to build exception + specifier. + * search.c (check_final_overrider): New static function, broken + out of get_matching_virtual. Check throw specifiers, reword + diagnostics. + (get_matching_virtual): Use check_final_overrider. + * tree.c (build_exception_variant): Use comp_except_specs. + * typeck.c (compexcepttypes): Remove. + (comp_except_types): New static function, helper for + comp_except_specs. Compare two types as exception specifiers. + (comp_except_specs): New global function, compare two exception + specifiers. + (comptypes): Adjust for comp_except_specs. + * typeck2.c (add_exception_specifier): New global function. + + * class.c (check_for_override): Reword error message. + 1999-08-03 Nathan Sidwell * call.c (convert_arg_to_ellipsis): Use pod_type_p. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 8ba5694525a..4f7172bc2be 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -2977,9 +2977,8 @@ check_for_override (decl, ctype) path to its virtual baseclass. */ if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE) { - cp_error_at ("method `%D' may not be declared static", - decl); - cp_error_at ("(since `%D' declared virtual in base class.)", + cp_error_at ("`static %#D' cannot be declared", decl); + cp_error_at (" since `virtual %#D' declared in base class", tmp); break; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 31e9f8cf047..5c7f7373653 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2288,6 +2288,7 @@ extern tree delta2_identifier; extern tree pfn_or_delta2_identifier; extern tree tag_identifier; extern tree vt_off_identifier; +extern tree empty_except_spec; /* A node that is a list (length 1) of error_mark_nodes. */ extern tree error_mark_list; @@ -3533,7 +3534,7 @@ extern int fntype_p PROTO((tree)); extern tree commonparms PROTO((tree, tree)); extern tree original_type PROTO((tree)); extern tree common_type PROTO((tree, tree)); -extern int compexcepttypes PROTO((tree, tree)); +extern int comp_except_specs PROTO((tree, tree, int)); extern int comptypes PROTO((tree, tree, int)); extern int comp_target_types PROTO((tree, tree, int)); extern int compparms PROTO((tree, tree)); @@ -3618,6 +3619,7 @@ extern tree build_functional_cast PROTO((tree, tree)); extern char *enum_name_string PROTO((tree, tree)); extern void report_case_error PROTO((int, tree, tree, tree)); extern void check_for_new_type PROTO((const char *, flagged_type_tree)); +extern tree add_exception_specifier PROTO((tree, tree, int)); /* in xref.c */ extern void GNU_xref_begin PROTO((const char *)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 498384b0ae6..6702f2c5edb 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -336,6 +336,9 @@ tree pfn_identifier, index_identifier, delta_identifier, delta2_identifier; tree pfn_or_delta2_identifier, tag_identifier; tree vt_off_identifier; +/* Exception specifier used for throw(). */ +tree empty_except_spec; + struct named_label_list { struct binding_level *binding_level; @@ -3478,11 +3481,12 @@ duplicate_decls (newdecl, olddecl) if ((pedantic || ! DECL_IN_SYSTEM_HEADER (olddecl)) && DECL_SOURCE_LINE (olddecl) != 0 && flag_exceptions - && ! compexcepttypes (TREE_TYPE (newdecl), TREE_TYPE (olddecl))) + && !comp_except_specs (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (newdecl)), + TYPE_RAISES_EXCEPTIONS (TREE_TYPE (olddecl)), 1)) { - cp_pedwarn ("declaration of `%D' throws different exceptions", + cp_error ("declaration of `%F' throws different exceptions", newdecl); - cp_pedwarn_at ("previous declaration here", olddecl); + cp_error_at ("to previous declaration `%F'", olddecl); } } TREE_TYPE (newdecl) = TREE_TYPE (olddecl) = newtype; @@ -6361,6 +6365,7 @@ init_decl_processing () const_string_type_node = build_pointer_type (build_qualified_type (char_type_node, TYPE_QUAL_CONST)); + empty_except_spec = build_tree_list (NULL_TREE, NULL_TREE); #if 0 record_builtin_type (RID_MAX, NULL_PTR, string_type_node); #endif @@ -6400,8 +6405,7 @@ init_decl_processing () c_common_nodes_and_builtins (1, flag_no_builtin, flag_no_nonansi_builtin); void_ftype_ptr - = build_exception_variant (void_ftype_ptr, - tree_cons (NULL_TREE, NULL_TREE, NULL_TREE)); + = build_exception_variant (void_ftype_ptr, empty_except_spec); /* C++ extensions */ @@ -6547,9 +6551,8 @@ init_decl_processing () if (flag_honor_std) pop_namespace (); newtype = build_exception_variant - (ptr_ftype_sizetype, build_tree_list (NULL_TREE, bad_alloc_type_node)); - deltype = build_exception_variant - (void_ftype_ptr, build_tree_list (NULL_TREE, NULL_TREE)); + (ptr_ftype_sizetype, add_exception_specifier (NULL_TREE, bad_alloc_type_node, -1)); + deltype = build_exception_variant (void_ftype_ptr, empty_except_spec); auto_function (ansi_opname[(int) NEW_EXPR], newtype, NOT_BUILT_IN); auto_function (ansi_opname[(int) VEC_NEW_EXPR], newtype, NOT_BUILT_IN); global_delete_fndecl diff --git a/gcc/cp/parse.y b/gcc/cp/parse.y index 9551c15d7dd..1fd3c5a1bf0 100644 --- a/gcc/cp/parse.y +++ b/gcc/cp/parse.y @@ -3685,21 +3685,22 @@ exception_specification_opt: | THROW '(' ansi_raise_identifiers ')' %prec EMPTY { $$ = $3; } | THROW LEFT_RIGHT %prec EMPTY - { $$ = build_decl_list (NULL_TREE, NULL_TREE); } + { $$ = empty_except_spec; } ; ansi_raise_identifier: type_id - { $$ = build_decl_list (NULL_TREE, groktypename($1.t)); } + { + check_for_new_type ("exception specifier", $1); + $$ = groktypename ($1.t); + } ; ansi_raise_identifiers: ansi_raise_identifier + { $$ = add_exception_specifier (NULL_TREE, $1, 1); } | ansi_raise_identifiers ',' ansi_raise_identifier - { - TREE_CHAIN ($3) = $$; - $$ = $3; - } + { $$ = add_exception_specifier ($1, $3, 1); } ; conversion_declarator: diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 4e9b4120040..b516cb9981f 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -3937,6 +3937,7 @@ lookup_template_class (d1, arglist, in_decl, context, entering_scope) type_decl = build_decl (TYPE_DECL, DECL_NAME (template), t); SET_DECL_ARTIFICIAL (type_decl); DECL_CONTEXT (type_decl) = TYPE_CONTEXT (t); + DECL_SOURCE_FILE (type_decl) = DECL_SOURCE_FILE (TYPE_STUB_DECL (template_type)); DECL_SOURCE_LINE (type_decl) @@ -6499,10 +6500,21 @@ tsubst (t, args, complain, in_decl) raises = TYPE_RAISES_EXCEPTIONS (t); if (raises) { - raises = tsubst (raises, args, complain, in_decl); - if (raises == error_mark_node) - return raises; - fntype = build_exception_variant (fntype, raises); + tree list = NULL_TREE; + + if (! TREE_VALUE (raises)) + list = raises; + else + for (; raises != NULL_TREE; raises = TREE_CHAIN (raises)) + { + tree spec = TREE_VALUE (raises); + + spec = tsubst (spec, args, complain, in_decl); + if (spec == error_mark_node) + return spec; + list = add_exception_specifier (list, spec, complain); + } + fntype = build_exception_variant (fntype, list); } return fntype; } diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 3af71f1cfc2..14e1ce9db4e 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -117,6 +117,7 @@ static tree get_virtuals_named_this PROTO((tree, tree)); static tree get_virtual_destructor PROTO((tree, void *)); static tree tree_has_any_destructor_p PROTO((tree, void *)); static int covariant_return_p PROTO((tree, tree)); +static int check_final_overrider PROTO((tree, tree)); static struct search_level *push_search_level PROTO((struct stack_level *, struct obstack *)); static struct search_level *pop_search_level @@ -1884,6 +1885,63 @@ covariant_return_p (brettype, drettype) return 1; } +/* Check that virtual overrider OVERRIDER is acceptable for base function + BASEFN. Issue diagnostic, and return zero, if unacceptable. */ + +int +check_final_overrider (overrider, basefn) + tree overrider, basefn; +{ + tree over_type = TREE_TYPE (overrider); + 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); + int i; + + if (same_type_p (base_return, over_return)) + /* OK */; + else if ((i = covariant_return_p (base_return, over_return))) + { + if (i == 2) + sorry ("adjusting pointers for covariant returns"); + + if (pedantic && i == -1) + { + cp_pedwarn_at ("invalid covariant return type for `virtual %#D'", overrider); + cp_pedwarn_at (" overriding `virtual %#D' (must be pointer or reference to class)", basefn); + } + } + else if (IS_AGGR_TYPE_2 (base_return, over_return) + && same_or_base_type_p (base_return, over_return)) + { + cp_error_at ("invalid covariant return type for `virtual %#D'", overrider); + cp_error_at (" overriding `virtual %#D' (must use pointer or reference)", basefn); + return 0; + } + else if (IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider)) == NULL_TREE) + { + cp_error_at ("conflicting return type specified for `virtual %#D'", overrider); + cp_error_at (" overriding `virtual %#D'", basefn); + SET_IDENTIFIER_ERROR_LOCUS (DECL_ASSEMBLER_NAME (overrider), + DECL_CLASS_CONTEXT (overrider)); + return 0; + } + + /* Check throw specifier is subset. */ + /* XXX At the moment, punt on an overriding artificial function. We + don't generate its exception specifier, so can't check it properly. */ + if (! DECL_ARTIFICIAL (overrider) + && !comp_except_specs (base_throw, over_throw, 0)) + { + cp_error_at ("looser throw specifier for `virtual %#F'", overrider); + cp_error_at (" overriding `virtual %#F'", basefn); + return 0; + } + return 1; +} + /* Given a class type TYPE, and a function decl FNDECL, look for a virtual function in TYPE's hierarchy which FNDECL could match as a virtual function. It doesn't matter which one we find. @@ -1897,7 +1955,6 @@ get_matching_virtual (binfo, fndecl, dtorp) int dtorp; { tree tmp = NULL_TREE; - int i; if (TREE_CODE (fndecl) == TEMPLATE_DECL) /* In [temp.mem] we have: @@ -1914,9 +1971,7 @@ get_matching_virtual (binfo, fndecl, dtorp) else { tree drettype, dtypes, btypes, instptr_type; - tree basetype = DECL_CLASS_CONTEXT (fndecl); tree baselink, best = NULL_TREE; - tree name = DECL_ASSEMBLER_NAME (fndecl); tree declarator = DECL_NAME (fndecl); if (IDENTIFIER_VIRTUAL_P (declarator) == 0) return NULL_TREE; @@ -1958,33 +2013,7 @@ get_matching_virtual (binfo, fndecl, dtorp) == TYPE_QUALS (instptr_type)) && compparms (TREE_CHAIN (btypes), TREE_CHAIN (dtypes))) { - tree brettype = TREE_TYPE (TREE_TYPE (tmp)); - if (same_type_p (brettype, drettype)) - /* OK */; - else if ((i = covariant_return_p (brettype, drettype))) - { - if (i == 2) - sorry ("adjusting pointers for covariant returns"); - - if (pedantic && i == -1) - { - cp_pedwarn_at ("invalid covariant return type for `%#D' (must be pointer or reference to class)", fndecl); - cp_pedwarn_at (" overriding `%#D'", tmp); - } - } - else if (IS_AGGR_TYPE_2 (brettype, drettype) - && same_or_base_type_p (brettype, drettype)) - { - error ("invalid covariant return type (must use pointer or reference)"); - cp_error_at (" overriding `%#D'", tmp); - cp_error_at (" with `%#D'", fndecl); - } - else if (IDENTIFIER_ERROR_LOCUS (name) == NULL_TREE) - { - cp_error_at ("conflicting return type specified for virtual function `%#D'", fndecl); - cp_error_at (" overriding definition as `%#D'", tmp); - SET_IDENTIFIER_ERROR_LOCUS (name, basetype); - } + check_final_overrider (fndecl, tmp); /* FNDECL overrides this function. We continue to check all the other functions in order to catch diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index ab714705b99..f40632afc33 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -1484,25 +1484,9 @@ build_exception_variant (type, raises) int type_quals = TYPE_QUALS (type); for (; v; v = TYPE_NEXT_VARIANT (v)) - { - tree t; - tree u; - - if (TYPE_QUALS (v) != type_quals) - continue; - - for (t = TYPE_RAISES_EXCEPTIONS (v), u = raises; - t != NULL_TREE && u != NULL_TREE; - t = TREE_CHAIN (t), u = TREE_CHAIN (u)) - if (((TREE_VALUE (t) != NULL_TREE) - != (TREE_VALUE (u) != NULL_TREE)) - || !same_type_p (TREE_VALUE (t), TREE_VALUE (u))) - break; - - if (!t && !u) - /* There's a memory leak here; RAISES is not freed. */ - return v; - } + if (TYPE_QUALS (v) == type_quals + && comp_except_specs (raises, TYPE_RAISES_EXCEPTIONS (v), 1)) + return v; /* Need to build a new variant. */ v = build_type_copy (type); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 582572aade9..03bc2c09f0c 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -48,6 +48,7 @@ static int comp_target_parms PROTO((tree, tree, int)); static int comp_ptr_ttypes_real PROTO((tree, tree, int)); static int comp_ptr_ttypes_const PROTO((tree, tree)); static int comp_ptr_ttypes_reinterpret PROTO((tree, tree)); +static int comp_except_types PROTO((tree, tree, int)); static int comp_array_types PROTO((int (*) (tree, tree, int), tree, tree, int)); static tree common_base_type PROTO((tree, tree)); @@ -853,13 +854,102 @@ common_type (t1, t2) } } -/* Return 1 if TYPE1 and TYPE2 raise the same exceptions. */ +/* Compare two exception specifier types for exactness or subsetness, if + allowed. Returns 0 for mismatch, 1 for same, 2 if B is allowed by A. + + [except.spec] "If a class X ... objects of class X or any class publicly + and unambigously derrived from X. Similarly, if a pointer type Y * ... + exceptions of type Y * or that are pointers to any type publicly and + unambigously derrived from Y. Otherwise a function only allows exceptions + that have the same type ..." + This does not mention cv qualifiers and is different to what throw + [except.throw] and catch [except.catch] will do. They will ignore the + top level cv qualifiers, and allow qualifiers in the pointer to class + example. + + We implement the letter of the standard. */ + +static int +comp_except_types (a, b, exact) + tree a, b; + int exact; +{ + if (same_type_p (a, b)) + return 1; + else if (!exact) + { + if (CP_TYPE_QUALS (a) || CP_TYPE_QUALS (b)) + return 0; + + if (TREE_CODE (a) == POINTER_TYPE + && TREE_CODE (b) == POINTER_TYPE) + { + a = TREE_TYPE (a); + b = TREE_TYPE (b); + if (CP_TYPE_QUALS (a) || CP_TYPE_QUALS (b)) + return 0; + } + + if (TREE_CODE (a) != RECORD_TYPE + || TREE_CODE (b) != RECORD_TYPE) + return 0; + + if (ACCESSIBLY_UNIQUELY_DERIVED_P (a, b)) + return 2; + } + return 0; +} + +/* Return 1 if TYPE1 and TYPE2 are equivalent exception specifiers. + If EXACT is 0, T2 can be a subset of T1 (according to 15.4/7), + otherwise it must be exact. Exception lists are unordered, but + we've already filtered out duplicates. Most lists will be in order, + we should try to make use of that. */ int -compexcepttypes (t1, t2) +comp_except_specs (t1, t2, exact) tree t1, t2; + int exact; { - return TYPE_RAISES_EXCEPTIONS (t1) == TYPE_RAISES_EXCEPTIONS (t2); + tree probe; + tree base; + int length = 0; + + if (t1 == t2) + return 1; + + if (t1 == NULL_TREE) /* T1 is ... */ + return t2 == NULL_TREE || !exact; + if (!TREE_VALUE (t1)) /* t1 is EMPTY */ + return t2 != NULL_TREE && !TREE_VALUE (t2); + if (t2 == NULL_TREE) /* T2 is ... */ + return 0; + if (TREE_VALUE(t1) && !TREE_VALUE (t2)) /* T2 is EMPTY, T1 is not */ + return !exact; + + /* Neither set is ... or EMPTY, make sure each part of T2 is in T1. + Count how many we find, to determine exactness. For exact matching and + ordered T1, T2, this is an O(n) operation, otherwise its worst case is + O(nm). */ + for (base = t1; t2 != NULL_TREE; t2 = TREE_CHAIN (t2)) + { + for (probe = base; probe != NULL_TREE; probe = TREE_CHAIN (probe)) + { + tree a = TREE_VALUE (probe); + tree b = TREE_VALUE (t2); + + if (comp_except_types (a, b, exact)) + { + if (probe == base && exact) + base = TREE_CHAIN (probe); + length++; + break; + } + } + if (probe == NULL_TREE) + return 0; + } + return !exact || base == NULL_TREE || length == list_length (t1); } /* Compare the array types T1 and T2, using CMP as the type comparison @@ -1031,7 +1121,8 @@ comptypes (t1, t2, strict) break; case METHOD_TYPE: - if (! compexcepttypes (t1, t2)) + if (! comp_except_specs (TYPE_RAISES_EXCEPTIONS (t1), + TYPE_RAISES_EXCEPTIONS (t2), 1)) return 0; /* This case is anti-symmetrical! @@ -1058,7 +1149,8 @@ comptypes (t1, t2, strict) break; case FUNCTION_TYPE: - if (! compexcepttypes (t1, t2)) + if (! comp_except_specs (TYPE_RAISES_EXCEPTIONS (t1), + TYPE_RAISES_EXCEPTIONS (t2), 1)) return 0; val = ((TREE_TYPE (t1) == TREE_TYPE (t2) @@ -2175,6 +2267,9 @@ build_component_ref (datum, component, basetype_path, protect) tree name = component; if (TREE_CODE (component) == VAR_DECL) name = DECL_NAME (component); + if (TREE_CODE (component) == NAMESPACE_DECL) + /* Source is in error, but produce a sensible diagnostic. */ + name = DECL_NAME (component); if (basetype_path == NULL_TREE) basetype_path = TYPE_BINFO (basetype); field = lookup_field (basetype_path, name, diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index e6a6088d7ab..cce836cfbbf 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1520,3 +1520,56 @@ check_for_new_type (string, inptree) && (pedantic || strcmp (string, "cast") != 0)) pedwarn ("ANSI C++ forbids defining types within %s",string); } + +/* Add new exception specifier SPEC, to the LIST we currently have. + If it's already in LIST then do nothing. + Moan if it's bad and we're allowed to. COMPLAIN < 0 means we + know what we're doing. */ + +tree +add_exception_specifier (list, spec, complain) + tree list, spec; + int complain; +{ + int ok; + tree core = spec; + int is_ptr; + + if (spec == error_mark_node) + return list; + + my_friendly_assert (spec && (!list || TREE_VALUE (list)), 19990317); + + /* [except.spec] 1, type in an exception specifier shall not be + incomplete, or pointer or ref to incomplete other than pointer + to cv void. */ + is_ptr = TREE_CODE (core) == POINTER_TYPE; + if (is_ptr || TREE_CODE (core) == REFERENCE_TYPE) + core = TREE_TYPE (core); + if (complain < 0) + ok = 1; + else if (TYPE_MAIN_VARIANT (core) == void_type_node) + ok = is_ptr; + else if (TREE_CODE (core) == TEMPLATE_TYPE_PARM) + ok = 1; + else + ok = TYPE_SIZE (core) != NULL_TREE; + + if (ok) + { + tree probe; + + for (probe = list; probe; probe = TREE_CHAIN (probe)) + if (same_type_p (TREE_VALUE (probe), spec)) + break; + if (!probe) + { + spec = build_decl_list (NULL_TREE, spec); + TREE_CHAIN (spec) = list; + list = spec; + } + } + else if (complain) + incomplete_type_error (NULL_TREE, core); + return list; +}