d: Add `@visibility' and `@hidden' attributes.
authorIain Buclaw <ibuclaw@gdcproject.org>
Wed, 15 Jun 2022 17:44:36 +0000 (19:44 +0200)
committerIain Buclaw <ibuclaw@gdcproject.org>
Wed, 15 Jun 2022 18:11:04 +0000 (20:11 +0200)
The `@visibility' attribute is functionality the same as
`__attribute__((visibility))', and `@hidden' is a convenience alias to
`@visibility("hidden")' defined in the `gcc.attributes' module.

As the visibility of a symbol is also indirectly controlled by the
`export' keyword, the handling of this in the code generation pass has
been improved so that conflicts will be appropriately diagnosed.

gcc/d/ChangeLog:

* d-attribs.cc (d_langhook_attribute_table): Add visibility.
(insert_type_attribute): Use decl_attributes instead of
merge_attributes.
(insert_decl_attribute): Likewise.
(apply_user_attributes): Do nothing when no UDAs applied.
(d_handle_visibility_attribute): New function.
* d-gimplify.cc (d_gimplify_binary_expr): Adjust.
* d-tree.h (set_visibility_for_decl): Declare.
* decl.cc (get_symbol_decl): Move setting of visibility flags to...
(set_visibility_for_decl): ... here.  New function.
* types.cc (TypeVisitor::visit (TypeStruct *)): Call
set_visibility_for_decl().
(TypeVisitor::visit (TypeClass *)): Likewise.

gcc/testsuite/ChangeLog:

* gdc.dg/attr_visibility1.d: New test.
* gdc.dg/attr_visibility2.d: New test.
* gdc.dg/attr_visibility3.d: New test.

libphobos/ChangeLog:

* libdruntime/gcc/attributes.d (visibility): Define.
(hidden): Define.

gcc/d/d-attribs.cc
gcc/d/d-gimplify.cc
gcc/d/d-tree.h
gcc/d/decl.cc
gcc/d/types.cc
gcc/testsuite/gdc.dg/attr_visibility1.d [new file with mode: 0644]
gcc/testsuite/gdc.dg/attr_visibility2.d [new file with mode: 0644]
gcc/testsuite/gdc.dg/attr_visibility3.d [new file with mode: 0644]
libphobos/libdruntime/gcc/attributes.d

index b8ce30c..4c6e7a7 100644 (file)
@@ -77,6 +77,7 @@ static tree d_handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_cold_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_restrict_attribute (tree *, tree, tree, int, bool *);
 static tree d_handle_used_attribute (tree *, tree, tree, int, bool *);
+static tree d_handle_visibility_attribute (tree *, tree, tree, int, bool *);
 
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)      \
@@ -223,6 +224,8 @@ const attribute_spec d_langhook_attribute_table[] =
             d_handle_restrict_attribute, NULL),
   ATTR_SPEC ("used", 0, 0, true, false, false, false,
             d_handle_used_attribute, NULL),
+  ATTR_SPEC ("visibility", 1, 1, false, false, false, false,
+            d_handle_visibility_attribute, NULL),
   ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL),
 };
 
@@ -238,10 +241,9 @@ insert_type_attribute (tree type, const char *attrname, tree value)
   if (value)
     value = tree_cons (NULL_TREE, value, NULL_TREE);
 
-  tree attribs = merge_attributes (TYPE_ATTRIBUTES (type),
-                                  tree_cons (ident, value, NULL_TREE));
-
-  return build_type_attribute_variant (type, attribs);
+  decl_attributes (&type, build_tree_list (ident, value),
+                  ATTR_FLAG_TYPE_IN_PLACE);
+  return type;
 }
 
 /* Insert the decl attribute ATTRNAME with value VALUE into DECL.  */
@@ -254,10 +256,9 @@ insert_decl_attribute (tree decl, const char *attrname, tree value)
   if (value)
     value = tree_cons (NULL_TREE, value, NULL_TREE);
 
-  tree attribs = merge_attributes (DECL_ATTRIBUTES (decl),
-                                  tree_cons (ident, value, NULL_TREE));
+  decl_attributes (&decl, build_tree_list (ident, value), 0);
 
-  return build_decl_attribute_variant (decl, attribs);
+  return decl;
 }
 
 /* Returns TRUE if NAME is an attribute recognized as being handled by
@@ -414,12 +415,7 @@ void
 apply_user_attributes (Dsymbol *sym, tree node)
 {
   if (!sym->userAttribDecl)
-    {
-      if (DECL_P (node) && DECL_ATTRIBUTES (node) != NULL)
-       decl_attributes (&node, DECL_ATTRIBUTES (node), 0);
-
-      return;
-    }
+    return;
 
   location_t saved_location = input_location;
   input_location = make_location_t (sym->loc);
@@ -1412,3 +1408,79 @@ d_handle_used_attribute (tree *node, tree name, tree, int, bool *no_add_attrs)
 
   return NULL_TREE;
 }
+
+/* Handle an "visibility" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+d_handle_visibility_attribute (tree *node, tree name, tree args,
+                              int, bool *)
+{
+  /*  If this is a type, set the visibility on the type decl.  */
+  tree decl = *node;
+  if (TYPE_P (decl))
+    {
+      decl = TYPE_NAME (decl);
+      if (decl == NULL_TREE || TREE_CODE (decl) != TYPE_DECL)
+       {
+         warning (OPT_Wattributes, "%qE attribute ignored on types", name);
+         return NULL_TREE;
+       }
+    }
+
+  if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      return NULL_TREE;
+    }
+
+  tree id = TREE_VALUE (args);
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error ("visibility argument not a string");
+      return NULL_TREE;
+    }
+
+  enum symbol_visibility vis;
+  if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
+    vis = VISIBILITY_DEFAULT;
+  else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
+    vis = VISIBILITY_INTERNAL;
+  else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
+    vis = VISIBILITY_HIDDEN;
+  else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
+    vis = VISIBILITY_PROTECTED;
+  else
+    {
+      error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
+            name, "default", "hidden", "protected", "internal");
+      vis = VISIBILITY_DEFAULT;
+    }
+
+  if (DECL_VISIBILITY_SPECIFIED (decl) && vis != DECL_VISIBILITY (decl))
+    {
+      tree attributes = (TYPE_P (*node)
+                        ? TYPE_ATTRIBUTES (*node)
+                        : DECL_ATTRIBUTES (decl));
+      if (lookup_attribute ("visibility", attributes))
+       error ("%qD redeclared with different visibility", decl);
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+              && lookup_attribute ("dllimport", attributes))
+       error ("%qD was declared %qs which implies default visibility",
+              decl, "export");
+      else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+              && lookup_attribute ("dllexport", attributes))
+       error ("%qD was declared %qs which implies default visibility",
+              decl, "export");
+    }
+
+  DECL_VISIBILITY (decl) = vis;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
+
+  /* Go ahead and attach the attribute to the node as well.  This is needed
+     so we can determine whether we have VISIBILITY_DEFAULT because the
+     visibility was not specified, or because it was explicitly overridden
+     from the containing scope.  */
+
+  return NULL_TREE;
+}
index 36b76da..33fe65c 100644 (file)
@@ -235,10 +235,10 @@ d_gimplify_binary_expr (tree *expr_p)
   if (bit_field_ref (op0) || bit_field_ref (op1))
     {
       if (TREE_TYPE (op0) != TREE_TYPE (*expr_p))
-       TREE_OPERAND (*expr_p, 0) = convert (TREE_TYPE (*expr_p), op0);
+       TREE_OPERAND (*expr_p, 0) = convert (TREE_TYPE (*expr_p), op0);
 
       if (TREE_TYPE (op1) != TREE_TYPE (*expr_p))
-       TREE_OPERAND (*expr_p, 1) = convert (TREE_TYPE (*expr_p), op1);
+       TREE_OPERAND (*expr_p, 1) = convert (TREE_TYPE (*expr_p), op1);
 
       return GS_OK;
     }
index 48a40a6..7517463 100644 (file)
@@ -654,6 +654,7 @@ extern tree build_artificial_decl (tree, tree, const char * = NULL);
 extern tree create_field_decl (tree, const char *, int, int);
 extern void build_type_decl (tree, Dsymbol *);
 extern void set_linkage_for_decl (tree);
+extern void set_visibility_for_decl (tree, Dsymbol *);
 
 /* In expr.cc.  */
 extern tree build_expr (Expression *, bool = false, bool = false);
index 518d84c..8676a1b 100644 (file)
@@ -1343,27 +1343,10 @@ get_symbol_decl (Declaration *decl)
   if (decl->storage_class & STCvolatile)
     TREE_THIS_VOLATILE (decl->csym) = 1;
 
-  /* Visibility attributes are used by the debugger.  */
-  if (decl->visibility.kind == Visibility::private_)
-    TREE_PRIVATE (decl->csym) = 1;
-  else if (decl->visibility.kind == Visibility::protected_)
-    TREE_PROTECTED (decl->csym) = 1;
-
   /* Likewise, so could the deprecated attribute.  */
   if (decl->storage_class & STCdeprecated)
     TREE_DEPRECATED (decl->csym) = 1;
 
-#if TARGET_DLLIMPORT_DECL_ATTRIBUTES
-  /* Have to test for import first.  */
-  if (decl->isImportedSymbol ())
-    {
-      insert_decl_attribute (decl->csym, "dllimport");
-      DECL_DLLIMPORT_P (decl->csym) = 1;
-    }
-  else if (decl->isExport ())
-    insert_decl_attribute (decl->csym, "dllexport");
-#endif
-
   if (decl->isDataseg () || decl->isCodeseg () || decl->isThreadlocal ())
     {
       /* Set TREE_PUBLIC by default, but allow private template to override.  */
@@ -1374,6 +1357,9 @@ get_symbol_decl (Declaration *decl)
       /* The decl has not been defined -- yet.  */
       DECL_EXTERNAL (decl->csym) = 1;
 
+      /* Visibility attributes are used by the debugger.  */
+      set_visibility_for_decl (decl->csym, decl);
+
       DECL_INSTANTIATED (decl->csym) = (decl->isInstantiated () != NULL);
       set_linkage_for_decl (decl->csym);
     }
@@ -2447,3 +2433,37 @@ set_linkage_for_decl (tree decl)
   if (DECL_ARTIFICIAL (decl))
     return d_weak_linkage (decl);
 }
+
+/* NODE is a FUNCTION_DECL, VAR_DECL or RECORD_TYPE for the declaration SYM.
+   Set flags to reflect visibility that NODE will get in the object file.  */
+
+void
+set_visibility_for_decl (tree node, Dsymbol *sym)
+{
+  Visibility visibility = sym->visible ();
+  if (visibility.kind == Visibility::private_)
+    TREE_PRIVATE (node) = 1;
+  else if (visibility.kind == Visibility::protected_)
+    TREE_PROTECTED (node) = 1;
+
+  /* If the declaration was declared `export', append either the dllimport
+     or dllexport attribute.  */
+  if (TARGET_DLLIMPORT_DECL_ATTRIBUTES)
+    {
+      const char *attrname = NULL;
+
+      /* Have to test for import first.  */
+      if (sym->isImportedSymbol ())
+       attrname = "dllimport";
+      else if (sym->isExport ())
+       attrname = "dllexport";
+
+      if (attrname != NULL)
+       {
+         if (DECL_P (node))
+           insert_decl_attribute (node, attrname);
+         else if (TYPE_P (node))
+           insert_type_attribute (node, attrname);
+       }
+    }
+}
index 0926715..b706c91 100644 (file)
@@ -1180,6 +1180,7 @@ public:
        /* Put out all fields.  */
        layout_aggregate_type (t->sym, t->ctype, t->sym);
        build_type_decl (t->ctype, t->sym);
+       set_visibility_for_decl (t->ctype, t->sym);
        apply_user_attributes (t->sym, t->ctype);
        finish_aggregate_type (structsize, alignsize, t->ctype);
       }
@@ -1224,6 +1225,7 @@ public:
     /* Put out all fields, including from each base class.  */
     layout_aggregate_type (t->sym, basetype, t->sym);
     build_type_decl (basetype, t->sym);
+    set_visibility_for_decl (basetype, t->sym);
     apply_user_attributes (t->sym, basetype);
     finish_aggregate_type (t->sym->structsize, t->sym->alignsize, basetype);
 
diff --git a/gcc/testsuite/gdc.dg/attr_visibility1.d b/gcc/testsuite/gdc.dg/attr_visibility1.d
new file mode 100644 (file)
index 0000000..a7ed406
--- /dev/null
@@ -0,0 +1,25 @@
+// { dg-do compile }
+// { dg-require-visibility "" }
+
+import gcc.attributes;
+
+void nested()
+{
+    @attribute("visibility", "default")
+    struct nested_struct { } // { dg-warning ".visibility. attribute ignored" }
+
+    @attribute("visibility", "default")
+    void nested_func() { } // { dg-warning ".visibility. attribute ignored" }
+}
+
+@attribute("visibility", 123)
+int not_a_string(); // { dg-error "visibility argument not a string" }
+
+@attribute("visibility", "invalid argument")
+int invalid_argument(); // { dg-error ".visibility. argument must be one of" }
+
+@attribute("visibility", "default")
+int redeclared_visibility();
+
+@attribute("visibility", "internal")
+int redeclared_visibility(); // { dg-error "redeclared with different visibility" }
diff --git a/gcc/testsuite/gdc.dg/attr_visibility2.d b/gcc/testsuite/gdc.dg/attr_visibility2.d
new file mode 100644 (file)
index 0000000..a339882
--- /dev/null
@@ -0,0 +1,26 @@
+// { dg-do compile }
+// { dg-require-visibility "" }
+
+module attr_visibility2;
+
+import gcc.attributes;
+
+// { dg-final { scan-hidden "_D16attr_visibility25func1FZv" } }
+
+@hidden void func1() { }
+
+// { dg-final { scan-hidden "_D16attr_visibility25func2FZv" } }
+
+@hidden void func2();
+
+void func2() { }
+
+// { dg-final { scan-hidden "_D16attr_visibility25func3FZv" } }
+
+void func3();
+
+@hidden void func3() { }
+
+// { dg-final { scan-hidden "_D16attr_visibility210globalvar1i" } }
+
+@hidden __gshared int globalvar1 = 5;
diff --git a/gcc/testsuite/gdc.dg/attr_visibility3.d b/gcc/testsuite/gdc.dg/attr_visibility3.d
new file mode 100644 (file)
index 0000000..3298428
--- /dev/null
@@ -0,0 +1,29 @@
+// { dg-do compile }
+// { dg-require-visibility "" }
+// { dg-require-dll "" }
+
+import gcc.attributes;
+
+@visibility("hidden")
+export void func1(); // { dg-error ".func1. was declared .export." }
+
+@visibility("hidden")
+export void func2() { } // { dg-error ".func2. was declared .export." }
+
+@visibility("default")
+export void func3();
+
+@visibility("default")
+export void func4() { };
+
+@visibility("hidden")
+export struct type1 { } // { dg-error ".type1. was declared .export." }
+
+@visibility("default")
+export struct type2 { }
+
+@visibility("hidden")
+export class type3 { } // { dg-error ".type3. was declared .export." }
+
+@visibility("default")
+export class type4 { }
index 09f684c..ca066ce 100644 (file)
@@ -425,6 +425,30 @@ auto target_clones(A...)(A arguments)
 enum used = attribute("used");
 
 /**
+ * The `@visibility` attribute affects the linkage of the declaration to which
+ * it is attached. It can be applied to variables, types, and functions.
+ *
+ * There are four supported visibility_type values: `default`, `hidden`,
+ * `protected`, or `internal` visibility.
+ *
+ * Example:
+ * ---
+ * import gcc.attributes;
+ *
+ * @visibility("protected") void func() {  }
+ * ---
+ */
+auto visibility(string visibilityName)
+{
+    return attribute("visibility", visibilityName);
+}
+
+auto visibility(A...)(A arguments)
+{
+    assert(false, "visibility attribute argument not a string constant");
+}
+
+/**
  * The `@weak` attribute causes a declaration of an external symbol to be
  * emitted as a weak symbol rather than a global.  This is primarily useful in
  * defining library functions that can be overridden in user code, though it can
@@ -543,6 +567,16 @@ enum dynamicCompileEmit = false;
 enum fastmath = optimize("Ofast");
 
 /**
+ * Sets the visibility of a function or global variable to "hidden".
+ * Such symbols aren't directly accessible from outside the DSO
+ * (executable or DLL/.so/.dylib) and are resolved inside the DSO
+ * during linking. If unreferenced within the DSO, the linker can
+ * strip a hidden symbol.
+ * An `export` visibility overrides this attribute.
+ */
+enum hidden = visibility("hidden");
+
+/**
  * Adds GCC's "naked" attribute to a function, disabling function prologue /
  * epilogue emission.
  * Intended to be used in combination with basic `asm` statement.  While using