diff mbox

[C++] Hidden functions

Message ID 3b33777d-ac48-f85c-ca47-f0d25cf26a06@acm.org
State New
Headers show

Commit Message

Nathan Sidwell May 24, 2017, 11:34 a.m. UTC
We have functions that are invisible to (most) name lookups:
1) anticipated builtins -- these only become visible once declared, but 
we need to know they match a builtin.
2) friends injected into namespace scope from a class definition.

This patch teaches the overload object about them with a new 
OVL_HIDDEN_P marker (so we can just look at the overload node), and more 
importantly it keeps them at the start of the overload list.  That'll 
make removing them from lookup results much simpler (a future patch).

Because of the ordering constraint, revealing a node is no longer so 
simple.  We may have to delete and reinsert it.  Thus a check after 
duplicate_decls discovers we're (re-)declaring a previously hidden decl.

The new testcases are to do with such unhiding.  anticipated builtins 
are always "C" functions, we need to do the check about extern "C" 
declarations matching.

nathan
diff mbox

Patch

2017-05-24  Nathan Sidwell  <nathan@acm.org>

	gcc/cp/
	* cp-tree.h (OVL_HIDDEN_P): New.
	(ovl_iterator::hidden_p, ovl_iterator::reveal_node): New.
	(ovl_iterator::reveal_node): Declare.
	* tree.c (ovl_copy): Copy OVL_HIDDEN_P.
	(ovl_insert): Order on hiddenness.
	(ovl_iterator::reveal_node): New.
	* name-lookup.c (anticipated_builtin_p): New.
	(supplement_binding_1): Use it.
	(set_local_extern_decl_linkage): Use hidden_p.
	(do_pushdecl): Deal with unhiding a hidden decl, use
	anticipated_builtin_p.
	(do_nonmember_using_decl): Use anticipated_decl_p.
	(lookup_name_real_1): Use DECL_HIDDEN_P.

	gcc/testsuite/
	* g++.dg/lookup/extern-c-hidden.C: New.
	* g++.dg/lookup/extern-redecl1.C: New.

Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h	(revision 248387)
+++ cp/cp-tree.h	(working copy)
@@ -378,6 +378,7 @@  extern GTY(()) tree cp_global_trees[CPTI
       REF_PARENTHESIZED_P (in COMPONENT_REF, INDIRECT_REF, SCOPE_REF)
       AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR)
       CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR)
+      OVL_HIDDEN_P (in OVERLOAD)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -659,6 +660,8 @@  typedef struct ptrmem_cst * ptrmem_cst_t
 
 /* If set, this was imported in a using declaration.   */
 #define OVL_USING_P(NODE)	TREE_LANG_FLAG_1 (OVERLOAD_CHECK (NODE))
+/* If set, this overload is a hidden decl.  */
+#define OVL_HIDDEN_P(NODE)	TREE_LANG_FLAG_2 (OVERLOAD_CHECK (NODE))
 /* If set, this overload contains a nested overload.  */
 #define OVL_NESTED_P(NODE)	TREE_LANG_FLAG_3 (OVERLOAD_CHECK (NODE))
 /* If set, this overload was constructed during lookup.  */
@@ -729,15 +732,26 @@  class ovl_iterator
   {
     return TREE_CODE (ovl) == OVERLOAD && OVL_USING_P (ovl);
   }
+  bool hidden_p () const
+  {
+    return TREE_CODE (ovl) == OVERLOAD && OVL_HIDDEN_P (ovl);
+  }
+
+ public:
   tree remove_node (tree head)
   {
     return remove_node (head, ovl);
   }
+  tree reveal_node (tree head)
+  {
+    return reveal_node (head, ovl);
+  }
 
  private:
-  /* We make this a static function to avoid the address of the
+  /* We make these static functions to avoid the address of the
      iterator escaping the local context.  */
   static tree remove_node (tree head, tree node);
+  static tree reveal_node (tree ovl, tree node);
 };
 
 /* Iterator over a (potentially) 2 dimensional overload, which is
Index: cp/name-lookup.c
===================================================================
--- cp/name-lookup.c	(revision 248387)
+++ cp/name-lookup.c	(working copy)
@@ -1077,6 +1077,26 @@  strip_using_decl (tree decl)
   return decl;
 }
 
+/* Return true if OVL is an overload for an anticipated builtin.  */
+
+static bool
+anticipated_builtin_p (tree ovl)
+{
+  if (TREE_CODE (ovl) != OVERLOAD)
+    return false;
+
+  if (!OVL_HIDDEN_P (ovl))
+    return false;
+
+  tree fn = OVL_FUNCTION (ovl);
+  gcc_checking_assert (DECL_ANTICIPATED (fn));
+
+  if (DECL_HIDDEN_FRIEND_P (fn))
+    return false;
+
+  return true;
+}
+
 /* BINDING records an existing declaration for a name in the current scope.
    But, DECL is another declaration for that same identifier in the
    same scope.  This is the `struct stat' hack whereby a non-typedef
@@ -1131,9 +1151,7 @@  supplement_binding_1 (cxx_binding *bindi
 	   || target_bval == error_mark_node
 	   /* If TARGET_BVAL is anticipated but has not yet been
 	      declared, pretend it is not there at all.  */
-	   || (TREE_CODE (target_bval) == FUNCTION_DECL
-	       && DECL_ANTICIPATED (target_bval)
-	       && !DECL_HIDDEN_FRIEND_P (target_bval)))
+	   || anticipated_builtin_p (target_bval))
     binding->value = decl;
   else if (TREE_CODE (target_bval) == TYPE_DECL
 	   && DECL_ARTIFICIAL (target_bval)
@@ -1803,7 +1821,7 @@  set_local_extern_decl_linkage (tree decl
 	loc_value = NULL_TREE;
 
       for (ovl_iterator iter (loc_value); iter; ++iter)
-	if (!DECL_HIDDEN_P (*iter)
+	if (!iter.hidden_p ()
 	    && (TREE_STATIC (*iter) || DECL_EXTERNAL (*iter))
 	    && decls_match (*iter, decl))
 	  {
@@ -1931,15 +1949,34 @@  do_pushdecl (tree decl, bool is_friend)
 	if (iter.using_p ())
 	  ; /* Ignore using decls here.  */
 	else if (tree match = duplicate_decls (decl, *iter, is_friend))
-	  return match;
+	  {
+	    if (iter.hidden_p ()
+		&& match != error_mark_node
+		&& !DECL_HIDDEN_P (match))
+	      {
+		/* Unhiding a previously hidden decl.  */
+		tree head = iter.reveal_node (old);
+		if (head != old)
+		  {
+		    if (!ns)
+		      update_local_overload (binding, head);
+		    binding->value = head;
+		  }
+
+		if (TREE_CODE (match) == FUNCTION_DECL
+		    && DECL_EXTERN_C_P (match))
+		  /* We need to check and register the fn now.  */
+		  check_extern_c_conflict (match);
+	      }
+	    return match;
+	  }
 
       /* We are pushing a new decl.  */
 
-      /* Skip a hidden builtin we failed to match already.  */
-      if (old && TREE_CODE (old) == FUNCTION_DECL
-	  && DECL_ANTICIPATED (old)
-	  && !DECL_HIDDEN_FRIEND_P (old))
-	old = NULL_TREE;
+      /* Skip a hidden builtin we failed to match already.  There can
+	 only be one.  */
+      if (old && anticipated_builtin_p (old))
+	old = OVL_CHAIN (old);
 
       check_template_shadow (decl);
 
@@ -3045,8 +3082,7 @@  do_nonmember_using_decl (tree scope, tre
 		    found = true;
 		  else if (old.using_p ())
 		    continue; /* This is a using decl. */
-		  else if (DECL_ANTICIPATED (old_fn)
-			   && !DECL_HIDDEN_FRIEND_P (old_fn))
+		  else if (old.hidden_p () && !DECL_HIDDEN_FRIEND_P (old_fn))
 		    continue; /* This is an anticipated builtin.  */
 		  else if (!matching_fn_p (new_fn, old_fn))
 		    continue; /* Parameters do not match.  */
@@ -3069,9 +3105,7 @@  do_nonmember_using_decl (tree scope, tre
 	}
       else if (value
 	       /* Ignore anticipated builtins.  */
-	       && !(TREE_CODE (value) == FUNCTION_DECL
-		    && DECL_ANTICIPATED (value)
-		    && !DECL_HIDDEN_FRIEND_P (value))
+	       && !anticipated_builtin_p (value)
 	       && !decls_match (lookup.value, value))
 	diagnose_name_conflict (lookup.value, value);
       else
@@ -5244,7 +5278,7 @@  lookup_name_real_1 (tree name, int prefe
 
 	if (binding)
 	  {
-	    if (hidden_name_p (binding))
+	    if (TREE_CODE (binding) == TYPE_DECL && DECL_HIDDEN_P (binding))
 	      {
 		/* A non namespace-scope binding can only be hidden in the
 		   presence of a local class, due to friend declarations.
Index: cp/tree.c
===================================================================
--- cp/tree.c	(revision 248386)
+++ cp/tree.c	(working copy)
@@ -2143,7 +2143,8 @@  ovl_copy (tree ovl)
   TREE_TYPE (result) = TREE_TYPE (ovl);
   OVL_FUNCTION (result) = OVL_FUNCTION (ovl);
   OVL_CHAIN (result) = OVL_CHAIN (ovl);
-  OVL_USING_P (ovl) = OVL_USING_P (ovl);
+  OVL_HIDDEN_P (result) = OVL_HIDDEN_P (ovl);
+  OVL_USING_P (result) = OVL_USING_P (ovl);
 
   return result;
 }
@@ -2156,14 +2157,16 @@  tree
 ovl_insert (tree fn, tree maybe_ovl, bool using_p)
 {
   bool copying = false; /* Checking use only.  */
-  int weight = using_p;
+  bool hidden_p = DECL_HIDDEN_P (fn);
+  int weight = (hidden_p << 1) | (using_p << 0);
 
   tree result = NULL_TREE;
   tree insert_after = NULL_TREE;
 
   /* Find insertion point.  */
   while (maybe_ovl && TREE_CODE (maybe_ovl) == OVERLOAD
-	 && (weight < OVL_USING_P (maybe_ovl)))
+	 && (weight < ((OVL_HIDDEN_P (maybe_ovl) << 1)
+		       | (OVL_USING_P (maybe_ovl) << 0))))
     {
       gcc_checking_assert (!OVL_LOOKUP_P (maybe_ovl)
 			   && (!OVL_USED_P (maybe_ovl) || !copying));
@@ -2181,9 +2184,11 @@  ovl_insert (tree fn, tree maybe_ovl, boo
     }
 
   tree trail = fn;
-  if (maybe_ovl || using_p || TREE_CODE (fn) == TEMPLATE_DECL)
+  if (maybe_ovl || using_p || hidden_p || TREE_CODE (fn) == TEMPLATE_DECL)
     {
       trail = ovl_make (fn, maybe_ovl);
+      if (hidden_p)
+	OVL_HIDDEN_P (trail) = true;
       if (using_p)
 	OVL_USING_P (trail) = true;
     }
@@ -2199,6 +2204,28 @@  ovl_insert (tree fn, tree maybe_ovl, boo
   return result;
 }
 
+/* NODE is an OVL_HIDDEN_P node which is now revealed.  */
+
+tree
+ovl_iterator::reveal_node (tree overload, tree node)
+{
+  /* We cannot have returned NODE as part of a lookup overload, so it
+     cannot be USED.  */
+  gcc_checking_assert (!OVL_USED_P (node));
+
+  OVL_HIDDEN_P (node) = false;
+  if (tree chain = OVL_CHAIN (node))
+    if (TREE_CODE (chain) == OVERLOAD
+	&& (OVL_USING_P (chain) || OVL_HIDDEN_P (chain)))
+      {
+	/* The node needs moving, and the simplest way is to remove it
+	   and reinsert.  */
+	overload = remove_node (overload, node);
+	overload = ovl_insert (OVL_FUNCTION (node), overload);
+      }
+  return overload;
+}
+
 /* NODE is on the overloads of OVL.  Remove it.  If a predecessor is
    OVL_USED_P we must copy OVL nodes, because those are immutable.
    The removed node is unaltered and may continue to be iterated
Index: testsuite/g++.dg/lookup/extern-c-hidden.C
===================================================================
--- testsuite/g++.dg/lookup/extern-c-hidden.C	(revision 0)
+++ testsuite/g++.dg/lookup/extern-c-hidden.C	(working copy)
@@ -0,0 +1,11 @@ 
+// Make sure unhidding an extern-c still checks it is compatible
+
+extern "C" float fabsf (float);  // { dg-error "conflicts with previous declaration" }
+
+namespace Bob 
+{
+  extern "C" float fabsf (float, float); // { dg-error "C language" }
+  extern "C" double fabs (double, double); // { dg-error "conflicts with previous declaration" }
+}
+
+extern "C" double fabs (double); // { dg-error "C language" }
Index: testsuite/g++.dg/lookup/extern-redecl1.C
===================================================================
--- testsuite/g++.dg/lookup/extern-redecl1.C	(revision 0)
+++ testsuite/g++.dg/lookup/extern-redecl1.C	(working copy)
@@ -0,0 +1,18 @@ 
+extern int X; // { dg-message "previous declaration" }
+extern int Y (int);  // { dg-message "previous declaration" }
+extern int Y (float);
+
+static int Z (int s)
+{
+  return s;
+}
+
+void Foo ()
+{
+  extern char X; // { dg-error "local external declaration" }
+  extern char Y (int); // { dg-error "local external declaration" }
+  extern int Y (float);
+  extern void Y (double);
+  extern char Z (int);
+}
+