aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/d/d-attribs.cc98
-rw-r--r--gcc/d/d-gimplify.cc4
-rw-r--r--gcc/d/d-tree.h1
-rw-r--r--gcc/d/decl.cc54
-rw-r--r--gcc/d/types.cc2
-rw-r--r--gcc/testsuite/gdc.dg/attr_visibility1.d25
-rw-r--r--gcc/testsuite/gdc.dg/attr_visibility2.d26
-rw-r--r--gcc/testsuite/gdc.dg/attr_visibility3.d29
-rw-r--r--libphobos/libdruntime/gcc/attributes.d34
9 files changed, 241 insertions, 32 deletions
diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
index b8ce30c..4c6e7a7 100644
--- a/gcc/d/d-attribs.cc
+++ b/gcc/d/d-attribs.cc
@@ -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;
+}
diff --git a/gcc/d/d-gimplify.cc b/gcc/d/d-gimplify.cc
index 36b76da..33fe65c 100644
--- a/gcc/d/d-gimplify.cc
+++ b/gcc/d/d-gimplify.cc
@@ -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;
}
diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
index 48a40a6..7517463 100644
--- a/gcc/d/d-tree.h
+++ b/gcc/d/d-tree.h
@@ -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);
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index 518d84c..8676a1b 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -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);
+ }
+ }
+}
diff --git a/gcc/d/types.cc b/gcc/d/types.cc
index 0926715..b706c91 100644
--- a/gcc/d/types.cc
+++ b/gcc/d/types.cc
@@ -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
index 0000000..a7ed406
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/attr_visibility1.d
@@ -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
index 0000000..a339882
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/attr_visibility2.d
@@ -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
index 0000000..3298428
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/attr_visibility3.d
@@ -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 { }
diff --git a/libphobos/libdruntime/gcc/attributes.d b/libphobos/libdruntime/gcc/attributes.d
index 09f684c..ca066ce 100644
--- a/libphobos/libdruntime/gcc/attributes.d
+++ b/libphobos/libdruntime/gcc/attributes.d
@@ -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