OSDN Git Service

* cp-tree.h (empty_except_spec): New global var.
authornathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 4 Aug 1999 09:07:51 +0000 (09:07 +0000)
committernathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 4 Aug 1999 09:07:51 +0000 (09:07 +0000)
(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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@28494 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/parse.y
gcc/cp/pt.c
gcc/cp/search.c
gcc/cp/tree.c
gcc/cp/typeck.c
gcc/cp/typeck2.c

index 74bde45..3617a86 100644 (file)
@@ -1,3 +1,33 @@
+1999-08-04  Nathan Sidwell  <nathan@acm.org>
+
+       * 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  <nathan@acm.org>
 
        * call.c (convert_arg_to_ellipsis): Use pod_type_p.
index 8ba5694..4f7172b 100644 (file)
@@ -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;
                }
index 31e9f8c..5c7f737 100644 (file)
@@ -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 *));
index 498384b..6702f2c 100644 (file)
@@ -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
index 9551c15..1fd3c5a 100644 (file)
@@ -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:
index 4e9b412..b516cb9 100644 (file)
@@ -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;
       }
index 3af71f1..14e1ce9 100644 (file)
@@ -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
index ab71470..f40632a 100644 (file)
@@ -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);
index 582572a..03bc2c0 100644 (file)
@@ -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)
     }
 }
 \f
-/* 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,
index e6a6088..cce836c 100644 (file)
@@ -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;
+}