diff mbox

[C++] Find conv ops by name

Message ID 7adf7a82-3679-464d-f153-48bc63647f7e@acm.org
State New
Headers show

Commit Message

Nathan Sidwell Aug. 25, 2017, 11:43 a.m. UTC
Conversion operators squirrel the target type on their IDENTIFIER_TYPE, 
thus have to have distinct IDENTIFIER_NODEs.  That doesn't work with 
find-by-name, and hence they currently have a unique slot in the 
METHOD_VEC -- yesterday's patch put them all on the same slot.

This patch allows them to be found by name.  I add conv_op_identifier, a 
unique identifier_node, and create conv_op_marker a unique and bogus 
function_decl.  The lookup machinery prepends an overload node pointing 
at this marker function to the set of conversion operators.

Thus we can now find them by name.  Either conv_op_identifier to get all 
the overloads.  Or by their unique conv-op identifiers to get just the 
conversion operators to that particular type.  In both cases the marker 
function is stripped off, so it is never seen.  The latter case works by 
post-processing the located overload set pulling out just those (usually 
at most one) converting the the required type.

And with that CLASSTYPE_FIRST_CONVERSION_SLOT is no more.  Applied to trunk.

nathan
diff mbox

Patch

2017-08-25  Nathan Sidwell  <nathan@acm.org>

	Conversion operators have a special name
	* cp-tree.h (CPTI_CONV_OP_MARKER, CPTI_CONV_OP_IDENTIFIER): New.
	(conv_op_marker, conv_op_identifier): New.
	(CLASSTYPE_FIRST_CONVERSION_SLOT): Delete.
	* decl.c (initialize_predefined_identifiers): Add
	conv_op_identifier.
	(cxx_init_decl_processing): Create conv_op_marker.
	* decl2.c (check_classfn): Lookup conv-ops by name.
	* class.c (add_method): Use conv_op_identifier & conv_op_marker.
	(resort_type_method_vec): Don't skip conv-ops.
	(finish_struct_methods, warn_hidden): Likewise.
	* name-lookup.h (lookup_all_conversions): Delete.
	* name-lookup.c (lookup_conversion_operator): Replace with ...
	(extract_conversion_operator): ... this.
	(lookup_fnfields_slot_nolazy): Find conv-ops by name.
	(lookup_all_conversions): Delete.
	* pt.c (check_explicit_specialization): Find conv-ops by name.
	* search.c (lookup_conversions_r): Likewise.

Index: class.c
===================================================================
--- class.c	(revision 251340)
+++ class.c	(working copy)
@@ -1017,9 +1017,6 @@  add_method (tree type, tree method, bool
   if (method == error_mark_node)
     return false;
 
-  bool complete_p = COMPLETE_TYPE_P (type);
-  bool conv_p = DECL_CONV_FN_P (method);
-
   vec<tree, va_gc> *method_vec = CLASSTYPE_METHOD_VEC (type);
   if (!method_vec)
     {
@@ -1032,32 +1029,45 @@  add_method (tree type, tree method, bool
   grok_special_member_properties (method);
 
   bool insert_p = true;
-  unsigned slot;
-  tree m;
+  tree method_name = DECL_NAME (method);
+  bool complete_p = COMPLETE_TYPE_P (type);
+  bool conv_p = IDENTIFIER_CONV_OP_P (method_name);
+
+  if (conv_p)
+    method_name = conv_op_identifier;
 
   /* See if we already have an entry with this name.  */
-  for (slot = CLASSTYPE_FIRST_CONVERSION_SLOT;
-       vec_safe_iterate (method_vec, slot, &m);
-       ++slot)
+  unsigned slot;
+  tree m;
+  for (slot = 0; vec_safe_iterate (method_vec, slot, &m); ++slot)
     {
-      m = OVL_FIRST (m);
-      if (conv_p)
-	{
-	  if (DECL_CONV_FN_P (m))
-	    insert_p = false;
-	  break;
-	}
-      if (DECL_NAME (m) == DECL_NAME (method))
+      m = DECL_NAME (OVL_FIRST (m));
+      if (m == method_name)
 	{
 	  insert_p = false;
 	  break;
 	}
-      if (complete_p
-	  && !DECL_CONV_FN_P (m)
-	  && DECL_NAME (m) > DECL_NAME (method))
+      if (complete_p && m > method_name)
 	break;
     }
   tree current_fns = insert_p ? NULL_TREE : (*method_vec)[slot];
+
+  tree conv_marker = NULL_TREE;
+  if (conv_p)
+    {
+      /* For conversion operators, we prepend a dummy overload
+	 pointing at conv_op_marker.  That function's DECL_NAME is
+	 conv_op_identifier, so we can use identifier equality to
+	 locate it.  */
+      if (current_fns)
+	{
+	  gcc_checking_assert (OVL_FUNCTION (current_fns) == conv_op_marker);
+	  conv_marker = current_fns;
+	  current_fns = OVL_CHAIN (current_fns);
+	}
+      else
+	conv_marker = ovl_make (conv_op_marker, NULL_TREE);
+    }
   gcc_assert (!DECL_EXTERN_C_P (method));
 
   /* Check to see if we've already got this method.  */
@@ -1206,7 +1216,12 @@  add_method (tree type, tree method, bool
   current_fns = ovl_insert (method, current_fns, via_using);
 
   if (conv_p)
-    TYPE_HAS_CONVERSION (type) = 1;
+    {
+      TYPE_HAS_CONVERSION (type) = 1;
+      /* Prepend the marker function.  */
+      OVL_CHAIN (conv_marker) = current_fns;
+      current_fns = conv_marker;
+    }
   else if (!complete_p && !IDENTIFIER_CDTOR_P (DECL_NAME (method)))
     push_class_level_binding (DECL_NAME (method), current_fns);
 
@@ -2294,23 +2309,10 @@  resort_type_method_vec (void* obj,
 {
   if (vec<tree, va_gc> *method_vec = (vec<tree, va_gc> *) obj)
     {
-      int len = method_vec->length ();
-      int slot;
-
-      /* The type conversion ops have to live at the front of the vec, so we
-	 can't sort them.  */
-      for (slot = CLASSTYPE_FIRST_CONVERSION_SLOT;
-	   slot < len; slot++)
-	if (!DECL_CONV_FN_P (OVL_FIRST ((*method_vec)[slot])))
-	  break;
-
-      if (len > slot + 1)
-	{
-	  resort_data.new_value = new_value;
-	  resort_data.cookie = cookie;
-	  qsort (method_vec->address () + slot, len - slot, sizeof (tree),
-		 resort_method_name_cmp);
-	}
+      resort_data.new_value = new_value;
+      resort_data.cookie = cookie;
+      qsort (method_vec->address (), method_vec->length (), sizeof (tree),
+	     resort_method_name_cmp);
     }
 }
 
@@ -2323,15 +2325,10 @@  resort_type_method_vec (void* obj,
 static void
 finish_struct_methods (tree t)
 {
-  vec<tree, va_gc> *method_vec;
-  int slot, len;
-
-  method_vec = CLASSTYPE_METHOD_VEC (t);
+  vec<tree, va_gc> *method_vec = CLASSTYPE_METHOD_VEC (t);
   if (!method_vec)
     return;
 
-  len = method_vec->length ();
-
   /* Clear DECL_IN_AGGR_P for all functions.  */
   for (tree fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn))
     if (DECL_DECLARES_FUNCTION_P (fn))
@@ -2341,17 +2338,8 @@  finish_struct_methods (tree t)
      no methods, then some public defaults are generated.  */
   maybe_warn_about_overly_private_class (t);
 
-  /* The type conversion ops have to live at the front of the vec, so we
-     can't sort them.  */
-  tree fn_fields;
-  for (slot = CLASSTYPE_FIRST_CONVERSION_SLOT;
-       method_vec->iterate (slot, &fn_fields);
-       ++slot)
-    if (!DECL_CONV_FN_P (OVL_FIRST (fn_fields)))
-      break;
-  if (len - slot > 1)
-    qsort (method_vec->address () + slot,
-	   len-slot, sizeof (tree), method_name_cmp);
+  qsort (method_vec->address (), method_vec->length (),
+	 sizeof (tree), method_name_cmp);
 }
 
 /* Make BINFO's vtable have N entries, including RTTI entries,
@@ -3000,12 +2988,9 @@  warn_hidden (tree t)
 {
   vec<tree, va_gc> *method_vec = CLASSTYPE_METHOD_VEC (t);
   tree fns;
-  size_t i;
 
   /* We go through each separately named virtual function.  */
-  for (i = CLASSTYPE_FIRST_CONVERSION_SLOT;
-       vec_safe_iterate (method_vec, i, &fns);
-       ++i)
+  for (int i = 0; vec_safe_iterate (method_vec, i, &fns); ++i)
     {
       tree fndecl;
       tree base_binfo;
Index: cp-tree.h
===================================================================
--- cp-tree.h	(revision 251339)
+++ cp-tree.h	(working copy)
@@ -125,6 +125,7 @@  enum cp_tree_index
     CPTI_TYPE_INFO_PTR_TYPE,
     CPTI_ABORT_FNDECL,
     CPTI_AGGR_TAG,
+    CPTI_CONV_OP_MARKER,
 
     CPTI_CTOR_IDENTIFIER,
     CPTI_COMPLETE_CTOR_IDENTIFIER,
@@ -133,6 +134,7 @@  enum cp_tree_index
     CPTI_COMPLETE_DTOR_IDENTIFIER,
     CPTI_BASE_DTOR_IDENTIFIER,
     CPTI_DELETING_DTOR_IDENTIFIER,
+    CPTI_CONV_OP_IDENTIFIER,
     CPTI_DELTA_IDENTIFIER,
     CPTI_IN_CHARGE_IDENTIFIER,
     CPTI_VTT_PARM_IDENTIFIER,
@@ -199,6 +201,7 @@  extern GTY(()) tree cp_global_trees[CPTI
 #define global_type_node		cp_global_trees[CPTI_GLOBAL_TYPE]
 #define const_type_info_type_node	cp_global_trees[CPTI_CONST_TYPE_INFO_TYPE]
 #define type_info_ptr_type		cp_global_trees[CPTI_TYPE_INFO_PTR_TYPE]
+#define conv_op_marker			cp_global_trees[CPTI_CONV_OP_MARKER]
 #define abort_fndecl			cp_global_trees[CPTI_ABORT_FNDECL]
 #define current_aggr			cp_global_trees[CPTI_AGGR_TAG]
 #define nullptr_node			cp_global_trees[CPTI_NULLPTR]
@@ -239,6 +242,10 @@  extern GTY(()) tree cp_global_trees[CPTI
 /* The name of a destructor that destroys virtual base classes, and
    then deletes the entire object.  */
 #define deleting_dtor_identifier	cp_global_trees[CPTI_DELETING_DTOR_IDENTIFIER]
+/* The name used for conversion operators -- but note that actual
+   conversion functions use special identifiers outside the identifier
+   table.  */
+#define conv_op_identifier		cp_global_trees[CPTI_CONV_OP_IDENTIFIER]
 
 /* The name of the identifier used internally to represent operator CODE.  */
 #define cp_operator_id(CODE) \
@@ -2148,10 +2155,6 @@  struct GTY(()) lang_type {
    and the RECORD_TYPE for the class template otherwise.  */
 #define CLASSTYPE_DECL_LIST(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->decl_list)
 
-/* The first slot in the CLASSTYPE_METHOD_VEC where conversion
-   operators can appear.  */
-#define CLASSTYPE_FIRST_CONVERSION_SLOT 0
-
 /* A FUNCTION_DECL or OVERLOAD for the constructors for NODE.  These
    are the constructors that take an in-charge parameter.  */
 #define CLASSTYPE_CONSTRUCTORS(NODE) \
Index: decl.c
===================================================================
--- decl.c	(revision 251339)
+++ decl.c	(working copy)
@@ -3979,6 +3979,7 @@  initialize_predefined_identifiers (void)
     {"__dt_base ", &base_dtor_identifier, cik_dtor},
     {"__dt_comp ", &complete_dtor_identifier, cik_dtor},
     {"__dt_del ", &deleting_dtor_identifier, cik_dtor},
+    {"__conv_op ", &conv_op_identifier, cik_conv_op},
     {"__in_chrg", &in_charge_identifier, cik_normal},
     {"this", &this_identifier, cik_normal},
     {"__delta", &delta_identifier, cik_normal},
@@ -4072,6 +4073,13 @@  cxx_init_decl_processing (void)
   noexcept_deferred_spec = build_tree_list (make_node (DEFERRED_NOEXCEPT),
 					    NULL_TREE);
 
+  /* Create the conversion operator marker.  This operator's DECL_NAME
+     is in the identifier table, so we can use identifier equality to
+     find it.  This has no type and no context, so we can't
+     accidentally think it a real function.  */
+  conv_op_marker = build_lang_decl (FUNCTION_DECL, conv_op_identifier,
+				    NULL_TREE);
+
 #if 0
   record_builtin_type (RID_MAX, NULL, string_type_node);
 #endif
Index: decl2.c
===================================================================
--- decl2.c	(revision 251339)
+++ decl2.c	(working copy)
@@ -664,7 +664,7 @@  check_classfn (tree ctype, tree function
       else
 	{
 	  if (DECL_CONV_FN_P (function))
-	    fns = lookup_all_conversions (ctype);
+	    fns = lookup_fnfields_slot (ctype, conv_op_identifier);
 
 	  error_at (DECL_SOURCE_LOCATION (function),
 		    "no declaration matches %q#D", function);
Index: name-lookup.c
===================================================================
--- name-lookup.c	(revision 251340)
+++ name-lookup.c	(working copy)
@@ -1089,37 +1089,27 @@  lookup_arg_dependent (tree name, tree fn
   return fns;
 }
 
-/* Return the conversion operators in CLASS_TYPE corresponding to
-   "operator TYPE ()".  Only CLASS_TYPE itself is searched; this
-   routine does not scan the base classes of CLASS_TYPE.  */
+/* FNS is an overload set of conversion functions.  Return the
+   overloads converting to TYPE.  */
 
 static tree
-lookup_conversion_operator (tree class_type, tree type)
+extract_conversion_operator (tree fns, tree type)
 {
   tree convs = NULL_TREE;
+  tree tpls = NULL_TREE;
 
-  if (TYPE_HAS_CONVERSION (class_type))
+  for (ovl_iterator iter (fns); iter; ++iter)
     {
-      tree fns = NULL_TREE;
-      tree tpls = NULL_TREE;
-      vec<tree, va_gc> *methods = CLASSTYPE_METHOD_VEC (class_type);
+      if (same_type_p (DECL_CONV_FN_TYPE (*iter), type))
+	convs = lookup_add (*iter, convs);
 
-      vec_safe_iterate (methods, CLASSTYPE_FIRST_CONVERSION_SLOT, &fns);
-      if (fns && !DECL_CONV_FN_P (OVL_FIRST (fns)))
-	fns = NULL_TREE;
-      for (ovl_iterator iter (fns); iter; ++iter)
-	{
-	  if (same_type_p (DECL_CONV_FN_TYPE (*iter), type))
-	    convs = lookup_add (*iter, convs);
-
-	  if (TREE_CODE (*iter) == TEMPLATE_DECL)
-	    tpls = lookup_add (*iter, tpls);
-	}
-
-      if (!convs)
-	convs = tpls;
+      if (TREE_CODE (*iter) == TEMPLATE_DECL)
+	tpls = lookup_add (*iter, tpls);
     }
 
+  if (!convs)
+    convs = tpls;
+
   return convs;
 }
 
@@ -1134,48 +1124,56 @@  lookup_fnfields_slot_nolazy (tree type,
   if (!method_vec)
     return NULL_TREE;
 
-  if (IDENTIFIER_CONV_OP_P (name))
-    return lookup_conversion_operator (type, TREE_TYPE (name));
-
-  /* Skip the conversion operators.  */
-  int i;
+  /* Conversion operators can only be found by the marker conversion
+     operator name.  */
+  bool conv_op = IDENTIFIER_CONV_OP_P (name);
+  tree lookup = conv_op ? conv_op_identifier : name;
+  tree val = NULL_TREE;
   tree fns;
-  for (i = CLASSTYPE_FIRST_CONVERSION_SLOT;
-       vec_safe_iterate (method_vec, i, &fns);
-       ++i)
-    if (!DECL_CONV_FN_P (OVL_FIRST (fns)))
-      break;
 
   /* If the type is complete, use binary search.  */
   if (COMPLETE_TYPE_P (type))
     {
-      int lo;
-      int hi;
-
-      lo = i;
-      hi = method_vec->length ();
+      int lo = 0;
+      int hi = method_vec->length ();
       while (lo < hi)
 	{
-	  i = (lo + hi) / 2;
+	  int i = (lo + hi) / 2;
 
 	  fns = (*method_vec)[i];
 	  tree fn_name = OVL_NAME (fns);
-	  if (fn_name > name)
+	  if (fn_name > lookup)
 	    hi = i;
-	  else if (fn_name < name)
+	  else if (fn_name < lookup)
 	    lo = i + 1;
 	  else
-	    return fns;
+	    {
+	      val = fns;
+	      break;
+	    }
 	}
     }
   else
-    for (; vec_safe_iterate (method_vec, i, &fns); ++i)
+    for (int i = 0; vec_safe_iterate (method_vec, i, &fns); ++i)
       {
-	if (OVL_NAME (fns) == name)
-	  return fns;
+	if (OVL_NAME (fns) == lookup)
+	  {
+	    val = fns;
+	    break;
+	  }
       }
 
-  return NULL_TREE;
+  /* Extract the conversion operators asked for, unless the general
+     conversion operator was requested.   */
+  if (val && conv_op)
+    {
+      gcc_checking_assert (OVL_FUNCTION (val) == conv_op_marker);
+      val = OVL_CHAIN (val);
+      if (tree type = TREE_TYPE (name))
+	val = extract_conversion_operator (val, type);
+    }
+
+  return val;
 }
 
 /* Do a 1-level search for NAME as a member of TYPE.  The caller must
@@ -1314,30 +1312,6 @@  lookup_fnfields_slot (tree type, tree na
   return lookup_fnfields_slot_nolazy (type, name);
 }
 
-/* Collect all the conversion operators of KLASS.  */
-
-tree
-lookup_all_conversions (tree klass)
-{
-  tree lkp = NULL_TREE;
-
-  if (vec<tree, va_gc> *methods = CLASSTYPE_METHOD_VEC (klass))
-    {
-      tree ovl;
-      for (int idx = CLASSTYPE_FIRST_CONVERSION_SLOT;
-	   methods->iterate (idx, &ovl); ++idx)
-	{
-	  if (!DECL_CONV_FN_P (OVL_FIRST (ovl)))
-	    /* There are no more conversion functions.  */
-	    break;
-
-	  lkp = lookup_add (ovl, lkp);
-	}
-    }
-
-  return lkp;
-}
-
 /* Compute the chain index of a binding_entry given the HASH value of its
    name and the total COUNT of chains.  COUNT is assumed to be a power
    of 2.  */
Index: name-lookup.h
===================================================================
--- name-lookup.h	(revision 251339)
+++ name-lookup.h	(working copy)
@@ -322,7 +322,6 @@  extern tree lookup_arg_dependent (tree,
 extern tree lookup_field_1			(tree, tree, bool);
 extern tree lookup_fnfields_slot		(tree, tree);
 extern tree lookup_fnfields_slot_nolazy		(tree, tree);
-extern tree lookup_all_conversions		(tree);
 extern tree innermost_non_namespace_value (tree);
 extern cxx_binding *outer_binding (tree, cxx_binding *, bool);
 extern void cp_emit_debug_info_for_using (tree, tree);
Index: pt.c
===================================================================
--- pt.c	(revision 251339)
+++ pt.c	(working copy)
@@ -2894,16 +2894,13 @@  check_explicit_specialization (tree decl
 	      name = DECL_NAME (decl);
 	    }
 
-	  tree fns = NULL_TREE;
-	  if (DECL_CONV_FN_P (decl))
-	    /* For a type-conversion operator, we cannot do a
-	       name-based lookup.  We might be looking for `operator
-	       int' which will be a specialization of `operator T'.
-	       Grab all the conversion operators, and then select from
-	       them.  */
-	    fns = lookup_all_conversions (ctype);
-	  else
-	    fns = lookup_fnfields_slot_nolazy (ctype, name);
+	  /* For a type-conversion operator, We might be looking for
+	     `operator int' which will be a specialization of
+	     `operator T'.  Grab all the conversion operators, and
+	     then select from them.  */
+	  tree fns = lookup_fnfields_slot_nolazy (ctype,
+						  IDENTIFIER_CONV_OP_P (name)
+						  ? conv_op_identifier : name);
 
 	  if (fns == NULL_TREE)
 	    {
Index: search.c
===================================================================
--- search.c	(revision 251340)
+++ search.c	(working copy)
@@ -2370,12 +2370,8 @@  lookup_conversions_r (tree binfo, int vi
     virtual_depth++;
 
   /* First, locate the unhidden ones at this level.  */
-  vec<tree, va_gc> *method_vec = CLASSTYPE_METHOD_VEC (BINFO_TYPE (binfo));
-  tree conv = NULL_TREE;
-  vec_safe_iterate (method_vec, CLASSTYPE_FIRST_CONVERSION_SLOT, &conv);
-  if (conv && !DECL_CONV_FN_P (OVL_FIRST (conv)))
-    conv = NULL_TREE;
-
+  tree conv = lookup_fnfields_slot_nolazy (BINFO_TYPE (binfo),
+					   conv_op_identifier);
   for (ovl_iterator iter (conv); iter; ++iter)
     {
       tree fn = *iter;