> On Nov 30, 2023, Jan Hubicka <hubi...@ucw.cz> wrote:
> 
> >> +      if (VAR_P (replaced))
> >> +  varpool_node::create_alias (sym_node->decl, replacement);
> >> +      else
> >> +  cgraph_node::create_alias (sym_node->decl, replacement);
> 
> Unfortunately, this change didn't work.  Several of the C++ tests
> regressed with it.  Going back to same-body aliases, they work.
> 
> I suspect this may have to do with the infrastructure put in to deal
> with cdtors clones.

Do you have short testcase for this?  THe main oddities with same body
aliases comes from the fact that C++ FE creates them early during
parsing before all declaration flags are finished.

Later we do:

  /* Ugly, but the fixup cannot happen at a time same body alias is created;
     C++ FE is confused about the COMDAT groups being right.  */
  if (symtab->cpp_implicit_aliases_done)
    FOR_EACH_SYMBOL (node)
      if (node->cpp_implicit_alias)
          node->fixup_same_cpp_alias_visibility (node->get_alias_target ());

Fixup copies some flags such as inline flags, visibility and comdat
groups which can change during parsing process.

Since you produce aliases late at finalization time, I do not see how
this could be relevant.  Pehraps unless you manage to copy wrong flags
from implicit aliases before the fixup happens which would be simple
ordering problem....

Honza
> 
> I've also found some discrepancies between C and C++ WRT sym_alias in
> static local variables, and failure to detect and report symbol name
> clashes between sym_aliases and unrelated declarations.  Thanks, Joseph,
> for pushing me to consider other cases I hadn't thought of before :-)
> I'm going to look into these, but for now, the patch below gets a full
> pass, with these issues XFAILed.
> 
> 
> > The IPA bits are fine.  I will take a look on your second patch.
> 
> Thanks!
> 
> 
> Introduce attribute sym_alias
> 
> This patch introduces an attribute to add extra asm names (aliases)
> for a decl when its definition is output.  The main goal is to ease
> interfacing C++ with Ada, as C++ mangled names have to be named, and
> in some cases (e.g. when using stdint.h typedefs in function
> arguments) the symbol names may vary across platforms.
> 
> The attribute is usable in C and C++, presumably in all C-family
> languages.  It can be attached to global variables and functions.  In
> C++, it can also be attached to class types, namespace-scoped
> variables and functions, static data members, member functions,
> explicit instantiations and specializations of template functions,
> members and classes.
> 
> When applied to constructors or destructor, additional sym aliases
> with _Base and _Del suffixes are defined for variants other than
> complete-object ones.  This changes the assumption that clones always
> carry the same attributes as their abstract declarations, so there is
> now a function to adjust them.
> 
> C++ also had a bug in which attributes from local extern declarations
> failed to be propagated to a preexisting corresponding
> namespace-scoped decl.  I've fixed that, and adjusted acc tests that
> distinguished between C and C++ in this regard.
> 
> Applying the attribute to class types is only valid in C++, and the
> effect is to attach the alias to the RTTI object associated with the
> class type.
> 
> for  gcc/ChangeLog
> 
>       * attribs.cc: Include cgraph.h.
>       (decl_attributes): Allow late introduction of sym_alias in
>       types.
>       (create_sym_alias_decl, create_sym_alias_decls): New.
>       * attribs.h: Declare them.
>       (FOR_EACH_SYM_ALIAS): New macro.
>       * cgraph.cc (cgraph_node::create): Create sym_alias decls.
>       * varpool.cc (varpool_node::get_create): Create sym_alias
>       decls.
>       * cgraph.h (symtab_node::remap_sym_alias_target): New.
>       * symtab.cc (symtab_node::remap_sym_alias_target): Define.
>       * cgraphunit.cc (cgraph_node::analyze): Create alias_target
>       node if needed.
>       (analyze_functions): Fixup visibility of implicit alias only
>       after its node is analyzed.
>       * doc/extend.texi (sym_alias): Document for variables,
>       functions and types.
> 
> for  gcc/ada/ChangeLog
> 
>       * doc/gnat_rm/interfacing_to_other_languages.rst: Mention
>       attribute sym_alias to give RTTI symbols mnemonic names.
>       * doc/gnat_ugn/the_gnat_compilation_model.rst: Mention
>       aliases.  Fix incorrect ref to C1 ctor variant.
> 
> for  gcc/c-family/ChangeLog
> 
>       * c-ada-spec.cc (pp_asm_name): Use first sym_alias if
>       available.
>       * c-attribs.cc (handle_sym_alias_attribute): New.
>       (c_common_attribute_table): Add sym_alias.
>       (handle_copy_attribute): Do not copy sym_alias attribute.
> 
> for  gcc/c/ChangeLog
> 
>       * c-decl.cc (duplicate_decls): Remap sym_alias target.
> 
> for  gcc/cp/ChangeLog
> 
>       * class.cc (adjust_clone_attributes): New.
>       (copy_fndecl_with_name, build_clone): Call it.
>       * cp-tree.h (adjust_clone_attributes): Declare.
>       (update_sym_alias_interface): Declare.
>       (update_tinfo_sym_alias): Declare.
>       * decl.cc (duplicate_decls): Remap sym_alias target.
>       Adjust clone attributes.
>       (grokfndecl): Tentatively create sym_alias decls after
>       adding attributes in e.g. a template member function explicit
>       instantiation.
>       * decl2.cc (cplus_decl_attributes): Update tinfo sym_alias.
>       (copy_interface, update_sym_alias_interface): New.
>       (determine_visibility): Update sym_alias interface.
>       (tentative_decl_linkage, import_export_decl): Likewise.
>       * name-lookup.cc: Include target.h and cgraph.h.
>       (push_local_extern_decl_alias): Merge attributes with
>       namespace-scoped decl, and drop duplicate sym_alias.
>       * optimize.cc (maybe_clone_body): Re-adjust attributes after
>       cloning them.  Update sym_alias interface.
>       * rtti.cc: Include attribs.h and cgraph.h.
>       (get_tinfo_decl): Copy sym_alias attributes from type to tinfo
>       decl.  Create sym_alias decls.
>       (update_tinfo_sym_alias): New.
> 
> for  gcc/testsuite/ChangeLog
> 
>       * c-c++-common/goacc/declare-1.c: Adjust.
>       * c-c++-common/goacc/declare-2.c: Adjust.
>       * c-c++-common/torture/attr-sym-alias-1.c: New.
>       * c-c++-common/torture/attr-sym-alias-2.c: New.
>       * c-c++-common/torture/attr-sym-alias-3.c: New.
>       * c-c++-common/torture/attr-sym-alias-4.c: New.
>       * c-c++-common/torture/attr-sym-alias-5.c: New.
>       * c-c++-common/attr-sym-alias-neg.c: New.
>       * g++.dg/torture/attr-sym-alias-1.C: New.
>       * g++.dg/torture/attr-sym-alias-2.C: New.
>       * g++.dg/torture/attr-sym-alias-3.C: New.
>       * g++.dg/torture/attr-sym-alias-4.C: New.
>       * g++.dg/torture/attr-sym-alias-5.C: New.
> ---
>  .../doc/gnat_rm/interfacing_to_other_languages.rst |    6 +
>  .../doc/gnat_ugn/the_gnat_compilation_model.rst    |   10 ++
>  gcc/attribs.cc                                     |   66 ++++++++++++++++
>  gcc/attribs.h                                      |    7 ++
>  gcc/c-family/c-ada-spec.cc                         |    7 ++
>  gcc/c-family/c-attribs.cc                          |   33 +++++++-
>  gcc/c/c-decl.cc                                    |    2 
>  gcc/cgraph.cc                                      |    2 
>  gcc/cgraph.h                                       |    4 +
>  gcc/cgraphunit.cc                                  |    2 
>  gcc/cp/class.cc                                    |   64 +++++++++++++++
>  gcc/cp/cp-tree.h                                   |    4 +
>  gcc/cp/decl.cc                                     |    4 +
>  gcc/cp/decl2.cc                                    |   50 ++++++++++++
>  gcc/cp/name-lookup.cc                              |   11 +++
>  gcc/cp/optimize.cc                                 |    3 +
>  gcc/cp/rtti.cc                                     |   71 +++++++++++++++++
>  gcc/doc/extend.texi                                |   56 +++++++++++++
>  gcc/symtab.cc                                      |   38 +++++++++
>  gcc/testsuite/c-c++-common/attr-sym-alias-neg.c    |   80 +++++++++++++++++++
>  gcc/testsuite/c-c++-common/goacc/declare-1.c       |    6 +
>  gcc/testsuite/c-c++-common/goacc/declare-2.c       |   14 ++-
>  .../c-c++-common/torture/attr-sym-alias-1.c        |   39 +++++++++
>  .../c-c++-common/torture/attr-sym-alias-2.c        |   13 +++
>  .../c-c++-common/torture/attr-sym-alias-3.c        |   41 ++++++++++
>  .../c-c++-common/torture/attr-sym-alias-4.c        |   28 +++++++
>  .../c-c++-common/torture/attr-sym-alias-5.c        |   54 +++++++++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C    |   72 +++++++++++++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C    |   26 ++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C    |   83 
> ++++++++++++++++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C    |   28 +++++++
>  gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C    |   14 +++
>  gcc/varpool.cc                                     |    3 +
>  33 files changed, 922 insertions(+), 19 deletions(-)
>  create mode 100644 gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
>  create mode 100644 gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
>  create mode 100644 gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
> 
> diff --git a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst 
> b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
> index ad0be511d4800..b8de16ebf7c0c 100644
> --- a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
> +++ b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst
> @@ -123,6 +123,12 @@ It is also possible to import a C++ exception using the 
> following syntax:
>  The ``External_Name`` is the name of the C++ RTTI symbol. You can then
>  cover a specific C++ exception in an exception handler.
>  
> +RTTI symbols undergo C++ name mangling, which can make for identifiers
> +that are inconvenient to use. An alias with a mnemonic name can be
> +introduced by adding attribute ``sym_alias`` to the class that the
> +RTTI symbol refers to.
> +
> +
>  .. _Interfacing_to_COBOL:
>  
>  Interfacing to COBOL
> diff --git a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst 
> b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
> index fd15459203a5c..2004569896268 100644
> --- a/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
> +++ b/gcc/ada/doc/gnat_ugn/the_gnat_compilation_model.rst
> @@ -4274,6 +4274,7 @@ and two public primitives to set and get the value of 
> this attribute.
>        public:
>          virtual void Set_Age (int New_Age);
>          virtual int Age ();
> +        __attribute__ ((__sym_alias__ ("Ctor_For_Animal")))
>          Animal() {Age_Count = 0;};
>        private:
>          int Age_Count;
> @@ -4306,6 +4307,7 @@ both Carnivore and Domestic, that is:
>          virtual int  Number_Of_Teeth ();
>          virtual void Set_Owner (char* Name);
>  
> +        __attribute__ ((__sym_alias__ ("Ctor_For_Dog"))) // mnemonic alias
>          Dog(); // Constructor
>        private:
>          int  Tooth_Count;
> @@ -4344,7 +4346,8 @@ how to import these C++ declarations from the Ada side:
>  
>         function New_Animal return Animal;
>         pragma CPP_Constructor (New_Animal);
> -       pragma Import (CPP, New_Animal, "_ZN6AnimalC1Ev");
> +       pragma Import (CPP, New_Animal,
> +                      "_ZN6AnimalC1Ev"); -- or "Ctor_For_Animal"
>  
>         type Dog is new Animal and Carnivore and Domestic with record
>           Tooth_Count : Natural;
> @@ -4360,7 +4363,7 @@ how to import these C++ declarations from the Ada side:
>  
>         function New_Dog return Dog;
>         pragma CPP_Constructor (New_Dog);
> -       pragma Import (CPP, New_Dog, "_ZN3DogC2Ev");
> +       pragma Import (CPP, New_Dog, "Ctor_For_Dog"); -- or "_ZN3DogC1Ev"
>       end Animals;
>  
>  Thanks to the compatibility between GNAT run-time structures and the C++ ABI,
> @@ -4382,7 +4385,8 @@ associated with each subprogram because it is assumed 
> that all the calls to
>  these primitives will be dispatching calls. The only exception is the
>  constructor, which must be registered with the compiler by means of
>  ``pragma CPP_Constructor`` and needs to provide its associated C++
> -mangled name because the Ada compiler generates direct calls to it.
> +mangled name (or an alias) because the Ada compiler generates direct
> +calls to it.
>  
>  With the above packages we can now declare objects of type Dog on the Ada 
> side
>  and dispatch calls to the corresponding subprograms on the C++ side. We can
> diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> index c7209c26acc9f..c75ca6974cb71 100644
> --- a/gcc/attribs.cc
> +++ b/gcc/attribs.cc
> @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "coretypes.h"
>  #include "target.h"
>  #include "tree.h"
> +#include "cgraph.h"
>  #include "stringpool.h"
>  #include "diagnostic-core.h"
>  #include "attribs.h"
> @@ -825,7 +826,8 @@ decl_attributes (tree *node, tree attributes, int flags,
>  
>        if (TYPE_P (*anode)
>         && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
> -       && COMPLETE_TYPE_P (*anode))
> +       && COMPLETE_TYPE_P (*anode)
> +       && !is_attribute_p ("sym_alias", name))
>       {
>         warning (OPT_Wattributes, "type attributes ignored after type is 
> already defined");
>         continue;
> @@ -2640,6 +2642,68 @@ attr_access::array_as_string (tree type) const
>    return typstr;
>  }
>  
> +/* Create a sym attribute for DECL to be visible with linkage name ID.  */
> +
> +tree
> +create_sym_alias_decl (tree decl, tree id)
> +{
> +  const char *attr_str = "sym_alias";
> +
> +  if (symtab_node *sym_node = symtab_node::get_for_asmname (id))
> +    {
> +      if ((sym_node->analyzed
> +        ? sym_node->get_alias_target ()->decl
> +        : sym_node->alias_target) == decl)
> +     return sym_node->decl;
> +
> +      tree attr_name = get_identifier (attr_str);
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +             "duplicate symbol name %qE in %qE attribute of %qD",
> +             id, attr_name, decl);
> +      inform (DECL_SOURCE_LOCATION (sym_node->decl),
> +           "already used by %qD", sym_node->decl);
> +    }
> +
> +  tree clone = copy_node (decl);
> +  DECL_ATTRIBUTES (clone) = remove_attribute (attr_str,
> +                                           DECL_ATTRIBUTES (decl));
> +  SET_DECL_ASSEMBLER_NAME (clone, id);
> +  TREE_USED (id) = 1;
> +  TREE_USED (clone) = 1;
> +  DECL_PRESERVE_P (clone) = 1;
> +  DECL_EXTERNAL (clone) = 0;
> +  TREE_STATIC (clone) = 1;
> +
> +  if (VAR_P (clone))
> +    // DECL_READ_P (clone) = 1;
> +    // varpool_node::create_extra_name_alias (clone, decl);
> +    varpool_node::create_alias (clone, decl);
> +  else
> +    cgraph_node::create_same_body_alias (clone, decl);
> +    // cgraph_node::create_alias (clone, decl);
> +
> +  return clone;
> +}
> +
> +/* Create decls for all sym aliases requested in DECL's attributes.  */
> +
> +void
> +create_sym_alias_decls (tree decl)
> +{
> +  if (!decl_in_symtab_p (decl)
> +      || !symtab_node::get (decl)
> +      || DECL_ABSTRACT_P (decl))
> +    return;
> +
> +  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (sym));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +
> +      create_sym_alias_decl (decl, id);
> +    }
> +}
> +
>  #if CHECKING_P
>  
>  namespace selftest
> diff --git a/gcc/attribs.h b/gcc/attribs.h
> index 84a43658a70da..156f2c2058800 100644
> --- a/gcc/attribs.h
> +++ b/gcc/attribs.h
> @@ -398,4 +398,11 @@ extern void init_attr_rdwr_indices (rdwr_map *, tree);
>  extern attr_access *get_parm_access (rdwr_map &, tree,
>                                    tree = current_function_decl);
>  
> +extern tree create_sym_alias_decl (tree, tree);
> +extern void create_sym_alias_decls (tree);
> +
> +#define FOR_EACH_SYM_ALIAS(sym, attrs)                                       
> \
> +  for (tree sym = lookup_attribute ("sym_alias", (attrs));           \
> +       sym; sym = lookup_attribute ("sym_alias", TREE_CHAIN (sym)))
> +
>  #endif // GCC_ATTRIBS_H
> diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
> index 050994d841665..5042b9cfecd80 100644
> --- a/gcc/c-family/c-ada-spec.cc
> +++ b/gcc/c-family/c-ada-spec.cc
> @@ -1431,6 +1431,13 @@ pp_ada_tree_identifier (pretty_printer *buffer, tree 
> node, tree type,
>  static void
>  pp_asm_name (pretty_printer *buffer, tree t)
>  {
> +  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (t))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (sym));
> +      pp_string (buffer, TREE_STRING_POINTER (id));
> +      return;
> +    }
> +
>    tree name = DECL_ASSEMBLER_NAME (t);
>    char *ada_name = XALLOCAVEC (char, IDENTIFIER_LENGTH (name) + 1), *s;
>    const char *ident = IDENTIFIER_POINTER (name);
> diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
> index 2b20e58c922c8..392437eab3b7d 100644
> --- a/gcc/c-family/c-attribs.cc
> +++ b/gcc/c-family/c-attribs.cc
> @@ -108,7 +108,8 @@ static tree handle_noplt_attribute (tree *, tree, tree, 
> int, bool *) ;
>  static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
>  static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
>  static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
> -static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ;
> +static tree handle_weakref_attribute (tree *, tree, tree, int, bool *);
> +static tree handle_sym_alias_attribute (tree *, tree, tree, int, bool *);
>  static tree handle_visibility_attribute (tree *, tree, tree, int,
>                                        bool *);
>  static tree handle_tls_model_attribute (tree *, tree, tree, int,
> @@ -388,6 +389,8 @@ const struct attribute_spec c_common_attribute_table[] =
>                             handle_alias_attribute, NULL },
>    { "weakref",                0, 1, true,  false, false, false,
>                             handle_weakref_attribute, NULL },
> +  { "sym_alias",              1, 1, false,  false, false, false,
> +                           handle_sym_alias_attribute, NULL },
>    { "no_instrument_function", 0, 0, true,  false, false, false,
>                             handle_no_instrument_function_attribute,
>                             NULL },
> @@ -3002,7 +3005,7 @@ handle_alias_ifunc_attribute (bool is_alias, tree 
> *node, tree name, tree args,
>    return NULL_TREE;
>  }
>  
> -/* Handle an "alias" or "ifunc" attribute; arguments as in
> +/* Handle an "ifunc" attribute; arguments as in
>     struct attribute_spec.handler.  */
>  
>  static tree
> @@ -3012,7 +3015,7 @@ handle_ifunc_attribute (tree *node, tree name, tree 
> args,
>    return handle_alias_ifunc_attribute (false, node, name, args, 
> no_add_attrs);
>  }
>  
> -/* Handle an "alias" or "ifunc" attribute; arguments as in
> +/* Handle an "alias" attribute; arguments as in
>     struct attribute_spec.handler.  */
>  
>  static tree
> @@ -3022,6 +3025,29 @@ handle_alias_attribute (tree *node, tree name, tree 
> args,
>    return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
>  }
>  
> +/* Handle a "sym_alias" attribute; arguments as in struct
> +   attribute_spec.handler.  */
> +
> +static tree
> +handle_sym_alias_attribute (tree *pnode, tree name, tree args,
> +                         int ARG_UNUSED (flags), bool *no_add_attrs)
> +{
> +  tree node = *pnode;
> +
> +  *no_add_attrs = true;
> +
> +  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
> +    error ("%qE attribute argument not a string", name);
> +  else if (decl_in_symtab_p (node))
> +    *no_add_attrs = false;
> +  else if (TYPE_P (node) && c_dialect_cxx ())
> +    *no_add_attrs = false;
> +  else
> +    return error_mark_node;
> +
> +  return NULL_TREE;
> +}
> +
>  /* Handle the "copy" attribute NAME by copying the set of attributes
>     from the symbol referenced by ARGS to the declaration of *NODE.  */
>  
> @@ -3155,6 +3181,7 @@ handle_copy_attribute (tree *node, tree name, tree args,
>             || is_attribute_p ("visibility", atname)
>             || is_attribute_p ("weak", atname)
>             || is_attribute_p ("weakref", atname)
> +           || is_attribute_p ("sym_alias", atname)
>             || is_attribute_p ("target_clones", atname))
>           continue;
>  
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index cf1df82c0f405..e5a1ee54cb3ee 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -3119,6 +3119,8 @@ duplicate_decls (tree newdecl, tree olddecl)
>  
>    merge_decls (newdecl, olddecl, newtype, oldtype);
>  
> +  symtab_node::remap_sym_alias_target (newdecl, olddecl);
> +
>    /* The NEWDECL will no longer be needed.
>  
>       Before releasing the node, be sure to remove function from symbol
> diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> index f93259a8c70c4..0bef2179e89c4 100644
> --- a/gcc/cgraph.cc
> +++ b/gcc/cgraph.cc
> @@ -523,6 +523,8 @@ cgraph_node::create (tree decl)
>    node->register_symbol ();
>    maybe_record_nested_function (node);
>  
> +  create_sym_alias_decls (decl);
> +
>    return node;
>  }
>  
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index cfdd9f693a889..cb5ac01f7e290 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -327,6 +327,10 @@ public:
>    /* Return DECL that alias is aliasing.  */
>    inline tree get_alias_target_tree ();
>  
> +  /* Remap sym alias nodes recorded as aliasing REPLACED to alias REPLACEMENT
> +     instead.  */
> +  static void remap_sym_alias_target (tree replaced, tree replacement);
> +
>    /* Set section for symbol and its aliases.  */
>    void set_section (const char *section);
>  
> diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
> index bccd2f2abb5a3..eb2d05094e989 100644
> --- a/gcc/cgraphunit.cc
> +++ b/gcc/cgraphunit.cc
> @@ -1175,7 +1175,7 @@ analyze_functions (bool first_time)
>       C++ FE is confused about the COMDAT groups being right.  */
>    if (symtab->cpp_implicit_aliases_done)
>      FOR_EACH_SYMBOL (node)
> -      if (node->cpp_implicit_alias)
> +      if (node->cpp_implicit_alias && node->analyzed)
>         node->fixup_same_cpp_alias_visibility (node->get_alias_target ());
>    build_type_inheritance_graph ();
>  
> diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
> index 6fdb56abfb9f8..fa8fc63db82a5 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -4896,6 +4896,68 @@ check_methods (tree t)
>      }
>  }
>  
> +/* Adjust sym alias name for CLONE, cloned from FN and named NAME,
> +   if it is a cdtor, and drop the sym alias from other clones.  */
> +
> +void
> +adjust_clone_attributes (tree fn, tree clone, tree name, bool skip_copy_p)
> +{
> +  if (IDENTIFIER_CDTOR_P (name))
> +    {
> +      bool found = false;
> +      FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
> +     {
> +       found = true;
> +       break;
> +     }
> +
> +      if (found
> +       && (name == complete_ctor_identifier
> +           || name == complete_dtor_identifier))
> +     {
> +       /* Reuse the sym alias decls created for the primary cdtor
> +          decl.  */
> +       symtab_node::remap_sym_alias_target (fn, clone);
> +     }
> +      else if (found)
> +     {
> +       const char *suf;
> +
> +       if (name == base_ctor_identifier
> +           || name == base_dtor_identifier)
> +         suf = "_Base";
> +       else if (name == deleting_dtor_identifier)
> +         suf = "_Del";
> +       else
> +         gcc_unreachable ();
> +
> +       size_t xlen = strlen (suf);
> +
> +       if (!skip_copy_p)
> +         DECL_ATTRIBUTES (clone) = copy_list (DECL_ATTRIBUTES (clone));
> +
> +       FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (clone))
> +         {
> +           /* We need to copy this even with skip_copy_p, because
> +              even then copying was shallow.  */
> +           TREE_VALUE (sym) = copy_list (TREE_VALUE (sym));
> +           /* Append suf to the sym alias name.  */
> +           tree str = TREE_VALUE (TREE_VALUE (sym));
> +           char *symname = concat (TREE_STRING_POINTER (str), suf, NULL);
> +           str = build_string (TREE_STRING_LENGTH (str) + xlen, symname);
> +           TREE_VALUE (TREE_VALUE (sym)) = str;
> +           free (symname);
> +         }
> +
> +       if (symtab_node::get (clone))
> +         create_sym_alias_decls (clone);
> +     }
> +    }
> +  else
> +    DECL_ATTRIBUTES (clone)
> +      = remove_attribute ("sym_alias", DECL_ATTRIBUTES (clone));
> +}
> +
>  /* FN is constructor, destructor or operator function.  Clone the
>     declaration to create a NAME'd variant.  NEED_VTT_PARM_P and
>     OMIT_INHERITED_PARMS_P are relevant if it's a cdtor.  */
> @@ -5065,6 +5127,8 @@ build_clone (tree fn, tree name, bool need_vtt_parm_p,
>    DECL_CHAIN (clone) = DECL_CHAIN (fn);
>    DECL_CHAIN (fn) = clone;
>  
> +  adjust_clone_attributes (fn, clone, name);
> +
>    return clone;
>  }
>  
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 964af1ddd85c5..2bafed4e9939b 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5787,6 +5787,8 @@ struct GTY((for_user)) spec_entry
>  
>  extern int current_class_depth;
>  
> +void adjust_clone_attributes (tree fn, tree clone, tree name, bool = false);
> +
>  /* in decl.cc */
>  
>  /* An array of static vars & fns.  */
> @@ -7022,6 +7024,7 @@ extern void do_push_parm_decls                  (tree, 
> tree, tree *);
>  extern tree do_aggregate_paren_init          (tree, tree);
>  
>  /* in decl2.cc */
> +extern void update_sym_alias_interface               (tree);
>  extern void record_mangling                  (tree, bool);
>  extern void overwrite_mangling                       (tree, tree);
>  extern void note_mangling_alias                      (tree, tree);
> @@ -7596,6 +7599,7 @@ extern bool emit_tinfo_decl                     (tree);
>  extern unsigned get_pseudo_tinfo_index               (tree);
>  extern tree get_pseudo_tinfo_type            (unsigned);
>  extern tree build_if_nonnull                 (tree, tree, tsubst_flags_t);
> +extern void update_tinfo_sym_alias           (tree);
>  
>  /* in search.cc */
>  extern tree get_parent_with_private_access   (tree decl, tree binfo);
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 2f2dbb8d10723..5636d694b6894 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -3237,6 +3237,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
> hiding, bool was_hidden)
>             && TREE_STATIC (olddecl))))
>      make_decl_rtl (olddecl);
>  
> +  symtab_node::remap_sym_alias_target (newdecl, olddecl);
> +
>    /* The NEWDECL will no longer be needed.  Because every out-of-class
>       declaration of a member results in a call to duplicate_decls,
>       freeing these nodes represents in a significant savings.
> @@ -3260,6 +3262,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
> hiding, bool was_hidden)
>        FOR_EACH_CLONE (clone, olddecl)
>       {
>         DECL_ATTRIBUTES (clone) = DECL_ATTRIBUTES (olddecl);
> +       adjust_clone_attributes (olddecl, clone, DECL_NAME (clone));
>         DECL_PRESERVE_P (clone) |= DECL_PRESERVE_P (olddecl);
>       }
>      }
> @@ -10789,6 +10792,7 @@ grokfndecl (tree ctype,
>      {
>        cplus_decl_attributes (&decl, *attrlist, 0);
>        *attrlist = NULL_TREE;
> +      create_sym_alias_decls (decl);
>      }
>  
>    if (DECL_HAS_CONTRACTS_P (decl))
> diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> index 9e666e5eecee0..c7f009beaf08e 100644
> --- a/gcc/cp/decl2.cc
> +++ b/gcc/cp/decl2.cc
> @@ -1844,6 +1844,9 @@ cplus_decl_attributes (tree *decl, tree attributes, int 
> flags)
>    if (late_attrs)
>      save_template_attributes (late_attrs, decl, flags);
>  
> +  if (TYPE_P (*decl) && attributes)
> +    update_tinfo_sym_alias (*decl);
> +
>    /* Propagate deprecation out to the template.  */
>    if (TREE_DEPRECATED (*decl))
>      if (tree ti = get_template_info (*decl))
> @@ -2200,6 +2203,47 @@ adjust_var_decl_tls_model (tree decl)
>      set_decl_tls_model (decl, decl_default_tls_model (decl));
>  }
>  
> +/* Copy externalness and linkage from DECL to DEST.  */
> +
> +static void
> +copy_interface (tree dest, tree decl)
> +{
> +  TREE_PUBLIC (dest) = TREE_PUBLIC (decl);
> +  TREE_STATIC (dest) = TREE_STATIC (decl);
> +  DECL_COMMON (dest) = DECL_COMMON (decl);
> +  DECL_COMDAT (dest) = DECL_COMDAT (decl);
> +  DECL_WEAK (dest) = DECL_WEAK (decl);
> +  DECL_EXTERNAL (dest) = DECL_EXTERNAL (decl);
> +  if (DECL_LANG_SPECIFIC (dest) && DECL_LANG_SPECIFIC (decl))
> +    DECL_NOT_REALLY_EXTERN (dest) = DECL_NOT_REALLY_EXTERN (decl);
> +  DECL_INTERFACE_KNOWN (dest) = DECL_INTERFACE_KNOWN (decl);
> +  DECL_VISIBILITY (dest) = DECL_VISIBILITY (decl);
> +  DECL_VISIBILITY_SPECIFIED (dest) = DECL_VISIBILITY_SPECIFIED (decl);
> +}
> +
> +/* Propagate linkage changes to sym aliases.  */
> +
> +void
> +update_sym_alias_interface (tree decl)
> +{
> +  if (!decl_in_symtab_p (decl)
> +      || !symtab_node::get (decl))
> +    return;
> +
> +  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (decl))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (sym));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +      symtab_node *sym_node = symtab_node::get_for_asmname (id);
> +
> +      if (sym_node
> +       && (sym_node->analyzed
> +           ? sym_node->get_alias_target ()->decl
> +           : sym_node->alias_target) == decl)
> +     copy_interface (sym_node->decl, decl);
> +    }
> +}
> +
>  /* Set DECL up to have the closest approximation of "initialized common"
>     linkage available.  */
>  
> @@ -3007,6 +3051,8 @@ determine_visibility (tree decl)
>         translation unit, we can make the type internal.  */
>      constrain_visibility (decl, VISIBILITY_ANON, false);
>  
> +  update_sym_alias_interface (decl);
> +
>    /* If visibility changed and DECL already has DECL_RTL, ensure
>       symbol flags are updated.  */
>    if ((DECL_VISIBILITY (decl) != orig_visibility
> @@ -3269,6 +3315,8 @@ tentative_decl_linkage (tree decl)
>        else if (VAR_P (decl))
>       maybe_commonize_var (decl);
>      }
> +
> +  update_sym_alias_interface (decl);
>  }
>  
>  /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
> @@ -3503,6 +3551,8 @@ import_export_decl (tree decl)
>      }
>  
>    DECL_INTERFACE_KNOWN (decl) = 1;
> +
> +  update_sym_alias_interface (decl);
>  }
>  
>  /* Return an expression that performs the destruction of DECL, which
> diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
> index d19ea5d121c8d..f877780b2acae 100644
> --- a/gcc/cp/name-lookup.cc
> +++ b/gcc/cp/name-lookup.cc
> @@ -22,9 +22,11 @@ along with GCC; see the file COPYING3.  If not see
>  #define INCLUDE_MEMORY
>  #include "system.h"
>  #include "coretypes.h"
> +#include "target.h"
>  #include "cp-tree.h"
>  #include "timevar.h"
>  #include "stringpool.h"
> +#include "cgraph.h"
>  #include "print-tree.h"
>  #include "attribs.h"
>  #include "debug.h"
> @@ -3479,6 +3481,15 @@ push_local_extern_decl_alias (tree decl)
>         /* Adjust visibility.  */
>         determine_visibility (alias);
>       }
> +      else if (DECL_P (alias))
> +     DECL_ATTRIBUTES (alias)
> +       = targetm.merge_decl_attributes (alias, decl);
> +      if (DECL_P (alias))
> +     {
> +       symtab_node::remap_sym_alias_target (decl, alias);
> +       DECL_ATTRIBUTES (decl)
> +         = remove_attribute ("sym_alias", DECL_ATTRIBUTES (alias));
> +     }
>      }
>  
>    retrofit_lang_decl (decl);
> diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc
> index 9e8926e4cc603..4a2e8cb435404 100644
> --- a/gcc/cp/optimize.cc
> +++ b/gcc/cp/optimize.cc
> @@ -528,9 +528,12 @@ maybe_clone_body (tree fn)
>        DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn);
>        DECL_DLLIMPORT_P (clone) = DECL_DLLIMPORT_P (fn);
>        DECL_ATTRIBUTES (clone) = clone_attrs (DECL_ATTRIBUTES (fn));
> +      adjust_clone_attributes (fn, clone, DECL_NAME (clone), true);
>        DECL_DISREGARD_INLINE_LIMITS (clone) = DECL_DISREGARD_INLINE_LIMITS 
> (fn);
>        set_decl_section_name (clone, fn);
>  
> +      update_sym_alias_interface (clone);
> +
>        /* Adjust the parameter names and locations.  */
>        parm = DECL_ARGUMENTS (fn);
>        clone_parm = DECL_ARGUMENTS (clone);
> diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
> index 7878929c24679..4bb7b8c8c6e78 100644
> --- a/gcc/cp/rtti.cc
> +++ b/gcc/cp/rtti.cc
> @@ -28,8 +28,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "stringpool.h"
>  #include "intl.h"
>  #include "stor-layout.h"
> +#include "attribs.h"
>  #include "c-family/c-pragma.h"
>  #include "gcc-rich-location.h"
> +#include "cgraph.h"
>  
>  /* C++ returns type information to the user in struct type_info
>     objects. We also use type information to implement dynamic_cast and
> @@ -479,8 +481,13 @@ get_tinfo_decl_direct (tree type, tree name, int 
> pseudo_ix)
>         = build_tree_list (get_identifier ("non overlapping"),
>                            NULL_TREE);
>        else
> +     /* Share the non overlapping attribute, without assuming it's
> +        the only attribute, but assuming it's the last if it's
> +        present.  There may be sym aliases too, and those are not
> +        to be shared.  */
>       DECL_ATTRIBUTES (d)
> -       = DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]);
> +       = lookup_attribute ("non overlapping",
> +                           DECL_ATTRIBUTES ((*unemitted_tinfo_decls)[0]));
>  
>        /* Mark the variable as undefined -- but remember that we can
>        define it later if we need to do so.  */
> @@ -492,6 +499,16 @@ get_tinfo_decl_direct (tree type, tree name, int 
> pseudo_ix)
>        if (CLASS_TYPE_P (type))
>       CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d;
>  
> +      /* Copy sym alias attributes from the type to the rtti obj decl.  */
> +      tree *attrs = &DECL_ATTRIBUTES (d);
> +      FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
> +     {
> +       tree attr = tree_cons (TREE_PURPOSE (sym), TREE_VALUE (sym), *attrs);
> +       *attrs = attr;
> +       attrs = &TREE_CHAIN (attr);
> +     }
> +      create_sym_alias_decls (d);
> +
>        /* Add decl to the global array of tinfo decls.  */
>        vec_safe_push (unemitted_tinfo_decls, d);
>      }
> @@ -499,6 +516,58 @@ get_tinfo_decl_direct (tree type, tree name, int 
> pseudo_ix)
>    return d;
>  }
>  
> +/* After modifying the attributes of TYPE, check whether tinfo was
> +   already created and, if so, add to it any sym alias attributes
> +   that were not already present.  */
> +
> +void
> +update_tinfo_sym_alias (tree type)
> +{
> +  if (!TYPE_SIZE (type) || !CLASS_TYPE_P (type))
> +    return;
> +
> +  tree d = CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type));
> +  if (!d)
> +    return;
> +
> +  bool first = true;
> +  symtab_node *node = NULL;
> +
> +  tree *attrs = &DECL_ATTRIBUTES (d);
> +  FOR_EACH_SYM_ALIAS (sym, TYPE_ATTRIBUTES (type))
> +    {
> +      bool found = false;
> +      FOR_EACH_SYM_ALIAS (d_sym, *attrs)
> +     if (TREE_VALUE (sym) == TREE_VALUE (d_sym))
> +       {
> +         found = true;
> +         break;
> +       }
> +
> +      if (found)
> +     continue;
> +
> +      tree attr = tree_cons (TREE_PURPOSE (sym),
> +                          TREE_VALUE (sym),
> +                          *attrs);
> +      *attrs = attr;
> +      attrs = &TREE_CHAIN (attr);
> +
> +      if (first)
> +     {
> +       first = false;
> +       node = symtab_node::get (d);
> +     }
> +
> +      if (!node)
> +     continue;
> +
> +      tree id = TREE_VALUE (TREE_VALUE (sym));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +      create_sym_alias_decl (d, id);
> +    }
> +}
> +
>  /* Return the type_info object for TYPE.  */
>  
>  tree
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index e6de0815846a6..5dcdec76c1ebc 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -4141,6 +4141,43 @@ Function Attributes}, @ref{PowerPC Function 
> Attributes},
>  @ref{Nios II Function Attributes}, and @ref{S/390 Function Attributes}
>  for details.
>  
> +@cindex @code{sym_alias} function attribute
> +@item sym_alias ("@var{name}")
> +The @code{sym_alias} attribute causes @var{name} to be output as an
> +alias to the function, with the same linkage and visibility as the
> +function, when the function definition is output, provided that the
> +function could be an alias target.  For instance,
> +
> +@smallexample
> +void f (uint64_t) __attribute__ ((__sym_alias__ ("f_u64")));
> +void f (uint64_t) @{ /* @r{Do something.} */; @}
> +@end smallexample
> +
> +@noindent
> +defines @samp{f}, and outputs @samp{f_u64} as an alias for @samp{f},
> +with the same linkage and visibility.  This is particularly useful when
> +exporting C++ names for use in other languages, or as an alias target,
> +when machine-dependent types would make mangled names harder to deal
> +with.
> +
> +In the case of C++ constructors and destructors, in which a single
> +definition may output multiple symbols, the specified name is associated
> +with the variant that constructs or destructs a complete object.  The
> +variant that applies to a base subobject gets a @code{_Base} suffix, and
> +the deleting destructor gets a @code{_Del} suffix.
> +
> +This attribute is silently ignored if @samp{f} is not defined in the
> +same translation unit, so that the attribute can be attached to forward
> +declarations.
> +
> +Aliases introduced with this attribute, such as @samp{f_u64} in the
> +example above, are assembly symbol names: they do not undergo C++ name
> +mangling, and are not made visible in any scope in the source language.
> +They can, however, can be named as alias targets.
> +
> +This attribute requires assembler and object file support for aliases,
> +and may not be available on all targets.
> +
>  @cindex @code{symver} function attribute
>  @item symver ("@var{name2}@@@var{nodename}")
>  On ELF targets this attribute creates a symbol version.  The @var{name2} part
> @@ -8069,6 +8106,10 @@ will be placed in new, unique sections.
>  
>  This additional functionality requires Binutils version 2.36 or later.
>  
> +@cindex @code{sym_alias} variable attribute
> +@item sym_alias ("@var{name}")
> +See @pxref{Common Function Attributes}.
> +
>  @cindex @code{uninitialized} variable attribute
>  @item uninitialized
>  This attribute, attached to a variable with automatic storage, means that
> @@ -9164,6 +9205,21 @@ is not supported; that is to say, if a given scalar 
> object can be accessed
>  through distinct types that assign a different storage order to it, then the
>  behavior is undefined.
>  
> +@cindex @code{sym_alias} type attribute
> +@item sym_alias ("@var{name}")
> +The @code{sym_alias} type attribute causes @var{name} to be emitted as an
> +alias to the definition of the C++ Run-Time Type Information (RTTI)
> +@code{std::type_info} object associated with the type.  For instance,
> +
> +@smallexample
> +class foo __attribute__ ((__sym_alias__ ("TI_foo")));
> +@end smallexample
> +
> +@noindent
> +arranges for @samp{TI_foo} to be defined as an alias to the RTTI object
> +for class @samp{foo}, once the class is defined and used in ways that
> +cause its RTTI object to be synthesized and output.
> +
>  @cindex @code{transparent_union} type attribute
>  @item transparent_union
>  
> diff --git a/gcc/symtab.cc b/gcc/symtab.cc
> index 0470509a98d2a..43ba88d793925 100644
> --- a/gcc/symtab.cc
> +++ b/gcc/symtab.cc
> @@ -1943,6 +1943,44 @@ symtab_node::noninterposable_alias (symtab_node *node, 
> void *data)
>    return false;
>  }
>  
> +/* Remap sym alias nodes recorded as aliasing REPLACED to alias
> +   REPLACEMENT instead.  */
> +
> +void
> +symtab_node::remap_sym_alias_target (tree replaced, tree replacement)
> +{
> +  if (!decl_in_symtab_p (replacement)
> +      || !symtab_node::get (replacement))
> +    return;
> +
> +  FOR_EACH_SYM_ALIAS (sym, DECL_ATTRIBUTES (replaced))
> +    {
> +      tree id = TREE_VALUE (TREE_VALUE (sym));
> +      id = get_identifier (TREE_STRING_POINTER (id));
> +
> +      symtab_node *sym_node = symtab_node::get_for_asmname (id);
> +
> +      if (!sym_node)
> +     {
> +       create_sym_alias_decl (replacement, id);
> +       continue;
> +     }
> +
> +      gcc_assert (!sym_node->analyzed);
> +      if (sym_node->alias_target != replaced)
> +     continue;
> +
> +      // sym_node->definition = 0;
> +
> +      if (VAR_P (replaced))
> +     // varpool_node::create_extra_name_alias (sym_node->decl, replacement);
> +     varpool_node::create_alias (sym_node->decl, replacement);
> +      else
> +     cgraph_node::create_same_body_alias (sym_node->decl, replacement);
> +     // cgraph_node::create_alias (sym_node->decl, replacement);
> +    }
> +}
> +
>  /* If node cannot be overwriten by static or dynamic linker to point to
>     different definition, return NODE. Otherwise look for alias with such
>     property and if none exists, introduce new one.  */
> diff --git a/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c 
> b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
> new file mode 100644
> index 0000000000000..f64e5bb1ac44b
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/attr-sym-alias-neg.c
> @@ -0,0 +1,80 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +int f () {
> +  int i __attribute__ ((sym_alias ("f_i"))); /* { dg-warning "ignored" } */
> +  /* ??? X cannot be an alias target; should this be flagged? ... */
> +  static int x __attribute__ ((sym_alias ("f_x")));
> +  static int sx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } 
> */
> +  extern int xx __attribute__ ((alias ("f_x"))); /* { dg-warning "ignored" } 
> */
> +  return i;
> +}
> +
> +/* ??? ... or should XR be accepted?  */
> +extern int xr __attribute__ ((alias ("f_x"))); /* { dg-error "undefined" "" 
> { xfail c++ } } */
> +int dr __attribute__ ((alias ("f_x"))); /* { dg-error "defined both" } */
> +
> +
> +struct s {
> +  int j __attribute__ ((sym_alias ("s_j"))); /* { dg-warning "ignored" } */
> +};
> +
> +
> +int j __attribute__ ((sym_alias ("J_var")));
> +void __attribute__ ((alias ("J_var")))
> +j_fn (); /* { dg-error "between function and variable" } */
> +
> +
> +void __attribute__ ((sym_alias ("K_fn")))
> +k () {
> +}
> +extern int __attribute__ ((alias ("K_fn")))
> +k_var; /* { dg-error "between function and variable" } */
> +
> +
> +int l __attribute__ ((sym_alias ("L_fn")));
> +
> +/* These should be detected and reported, not because the names clash at the
> +   source level, but because the asm symbols do.  */
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +void
> +L_fn () /* { dg-error "duplicate" "" { xfail *-*-* } } */
> +{
> +}
> +
> +
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +void __attribute__ ((sym_alias ("M_var")))
> +m ()
> +{
> +}
> +
> +int M_var; /* { dg-error "duplicate" "" { xfail *-*-* } } */
> +
> +
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +void __attribute__ ((sym_alias ("N_sym")))
> +n_fn ()
> +{
> +}
> +
> +int __attribute__ ((sym_alias ("N_sym")))
> +n_var; /* { dg-error "duplicate" } */
> +
> +
> +int __attribute__ ((sym_alias ("O_sym")))
> +o_var;
> +
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +void __attribute__ ((sym_alias ("O_sym")))
> +o_fn () /* { dg-error "duplicate" } */
> +{
> +}
> diff --git a/gcc/testsuite/c-c++-common/goacc/declare-1.c 
> b/gcc/testsuite/c-c++-common/goacc/declare-1.c
> index 46ee01b675950..f284289331807 100644
> --- a/gcc/testsuite/c-c++-common/goacc/declare-1.c
> +++ b/gcc/testsuite/c-c++-common/goacc/declare-1.c
> @@ -113,11 +113,11 @@ f_2 (void)
>    int va3;
>  #pragma acc declare device_resident(va3)
>  
> -#ifndef __cplusplus
> +#if 0
>    /* TODO PR90868
>  
> -     C: "error: variable '[...]' used more than once with '#pragma acc 
> declare'".  */
> -#else
> +     "error: variable '[...]' used more than once with '#pragma acc 
> declare'".  */
> +
>    extern int ve0;
>  #pragma acc declare create(ve0)
>  
> diff --git a/gcc/testsuite/c-c++-common/goacc/declare-2.c 
> b/gcc/testsuite/c-c++-common/goacc/declare-2.c
> index e2e22be57e9e4..aec59b69754c5 100644
> --- a/gcc/testsuite/c-c++-common/goacc/declare-2.c
> +++ b/gcc/testsuite/c-c++-common/goacc/declare-2.c
> @@ -137,25 +137,25 @@ void
>  f_pr90868_2 (void)
>  {
>    extern int we0;
> -#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than 
> once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare create(we0) /* { dg-error "variable 'we0' used more than 
> once with '#pragma acc declare'" "" } */
>  
>    extern int we1;
> -#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than 
> once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare copyin(we1) /* { dg-error "variable 'we1' used more than 
> once with '#pragma acc declare'" "" } */
>  
>    extern int *we2;
> -#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more 
> than once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare deviceptr(we2) /* { dg-error "variable 'we2' used more 
> than once with '#pragma acc declare'" "" } */
>  
>    extern int we3;
> -#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used 
> more than once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare device_resident(we3) /* { dg-error "variable 'we3' used 
> more than once with '#pragma acc declare'" "" } */
>  
>    extern int we4;
> -#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than 
> once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare link(we4) /* { dg-error "variable 'we4' used more than 
> once with '#pragma acc declare'" "" } */
>  
>    extern int we5;
> -#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' 
> used more than once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare present_or_copyin(we5) /* { dg-error "variable 'we5' 
> used more than once with '#pragma acc declare'" "" } */
>   
>    extern int we6;
> -#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' 
> used more than once with '#pragma acc declare'" "" { target c } } */
> +#pragma acc declare present_or_create(we6) /* { dg-error "variable 'we6' 
> used more than once with '#pragma acc declare'" "" } */
>  }
>  
>  
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c 
> b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
> new file mode 100644
> index 0000000000000..61af50cb5527f
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-1.c
> @@ -0,0 +1,39 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
> +int var_a = 1;
> +
> +void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
> +
> +void
> +foo_a ()
> +{
> +}
> +
> +
> +int var_b;
> +extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
> +
> +void
> +foo_b ()
> +{
> +}
> +
> +void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
> +
> +
> +int var_c __attribute__ ((__sym_alias__ ("FOOVAR_C")));
> +
> +void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
> +foo_c ()
> +{
> +}
> +
> +
> +/* { dg-final { scan-assembler "FOOBAR_A" } } */
> +/* { dg-final { scan-assembler "FOOVAR_A" } } */
> +/* { dg-final { scan-assembler "FOOBAR_B" } } */
> +/* { dg-final { scan-assembler "FOOVAR_B" } } */
> +/* { dg-final { scan-assembler "FOOBAR_C" } } */
> +/* { dg-final { scan-assembler "FOOVAR_C" } } */
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c 
> b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
> new file mode 100644
> index 0000000000000..6f0d55ca42cc8
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-2.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +struct s
> +{
> +  int mem __attribute__ ((__sym_alias__ ("MEMFOO"))); /* { dg-warning 
> "attribute ignored" } */
> +};
> +
> +void foo()
> +{
> +  extern void bar () __attribute__ ((__sym_alias__ ("FOOBAR")));
> +  int var __attribute__ ((__sym_alias__ ("FOOVAR"))); /* { dg-warning 
> "attribute ignored" } */
> +}
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c 
> b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
> new file mode 100644
> index 0000000000000..0052485a30afb
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-3.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +int var_a = 1;
> +
> +void
> +foo_a ()
> +{
> +  extern int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A")));
> +  void foo_a () __attribute__ ((__sym_alias__ ("FOOBAR_A")));
> +}
> +
> +#if 0 // __cplusplus
> +/* Without this declaration before the local declaration below, the
> +   attributes of the local declaration do not get propagated to the
> +   (global) namespace scope.  */
> +extern int var_b;
> +#endif
> +
> +void
> +foo_b ()
> +{
> +  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_B")));
> +}
> +
> +int var_b;
> +
> +void __attribute__ ((__sym_alias__ ("FOOBAR_C")))
> +foo_c ()
> +{
> +  void foo_b () __attribute__ ((__sym_alias__ ("FOOBAR_B")));
> +  /* Another sym for var_b.  */
> +  extern int var_b __attribute__ ((__sym_alias__ ("FOOVAR_C")));
> +}
> +
> +/* { dg-final { scan-assembler "FOOBAR_A" } } */
> +/* { dg-final { scan-assembler "FOOVAR_A" } } */
> +/* { dg-final { scan-assembler "FOOBAR_B" } } */
> +/* { dg-final { scan-assembler "FOOVAR_B" } } */
> +/* { dg-final { scan-assembler "FOOBAR_C" } } */
> +/* { dg-final { scan-assembler "FOOVAR_C" } } */
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c 
> b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
> new file mode 100644
> index 0000000000000..2beafa4637923
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-4.c
> @@ -0,0 +1,28 @@
> +/* { dg-do run } */
> +/* { dg-require-alias "" } */
> +
> +int var_a __attribute__ ((__sym_alias__ ("FOOVAR_A"))) = 42;
> +
> +int __attribute__ ((__sym_alias__ ("FOOBAR_A")))
> +foo_a (int p)
> +{
> +  return p;
> +}
> +
> +extern int __attribute__ ((__alias__ (("FOOVAR_A")))) var_b;
> +extern int __attribute__ ((__alias__ (("FOOBAR_A")))) foo_b (int p);
> +
> +int
> +foo_c ()
> +{
> +  return foo_b (var_b);
> +}
> +
> +int
> +main ()
> +{
> +  if (foo_c () != 42)
> +    __builtin_abort ();
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c 
> b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
> new file mode 100644
> index 0000000000000..bee90136dafd5
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/torture/attr-sym-alias-5.c
> @@ -0,0 +1,54 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +/* { dg-require-visibility "" } */
> +
> +int __attribute__ ((sym_alias ("A_hid"), visibility ("hidden"))) a;
> +
> +extern int __attribute__ ((sym_alias ("B_prt"))) b;
> +
> +int b __attribute__ ((visibility ("protected")));
> +
> +extern int __attribute__ ((sym_alias ("C_ntr"))) c;
> +
> +extern int c __attribute__ ((visibility ("internal")));
> +
> +int c;
> +
> +static int d __attribute__ ((sym_alias ("D_stt")));
> +
> +
> +extern void __attribute__ ((sym_alias ("F_hid"), visibility ("hidden")))
> +f ();
> +
> +void
> +f () {
> +}
> +
> +extern void __attribute__ ((sym_alias ("G_prt")))
> +g ();
> +
> +void __attribute__ ((visibility ("protected")))
> +g () {
> +}
> +
> +extern void __attribute__ ((sym_alias ("H_ntr")))
> +h ();
> +
> +void __attribute__ ((visibility ("internal")))
> +h ();
> +
> +void h () {
> +}
> +
> +static void __attribute__ ((sym_alias ("I_stt")))
> +i () {
> +}
> +
> +/* { dg-final { scan-assembler {hidden[^\n]*A_hid} } } */
> +/* { dg-final { scan-assembler {protected[^\n]*B_prt} } } */
> +/* { dg-final { scan-assembler {internal[^\n]*C_ntr} } } */
> +/* { dg-final { scan-assembler-not {glob[^\n]*D_stt} } } */
> +/* { dg-final { scan-assembler {hidden[^\n]*F_hid} } } */
> +/* { dg-final { scan-assembler {protected[^\n]*G_prt} } } */
> +/* { dg-final { scan-assembler {internal[^\n]*H_ntr} } } */
> +/* { dg-final { scan-assembler-not {glob[^\n]*I_stt} } } */
> diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C 
> b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
> new file mode 100644
> index 0000000000000..579eda1a473f6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-1.C
> @@ -0,0 +1,72 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +class __attribute__ ((__sym_alias__ ("FOOCLS_A"),
> +                   __sym_alias__ ("FOOCLS_A_Dupe"))) foo {
> +  static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
> +  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
> +  void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
> +  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
> +};
> +
> +int foo::var = 1;
> +
> +foo::foo () {}
> +
> +void foo::bar () {}
> +
> +namespace b {
> +  class __attribute__ ((__sym_alias__ ("FOOCLS_B"))) foo {
> +    static int var __attribute__ ((__sym_alias__ ("FOOVAR_B")));
> +    __attribute__ ((__sym_alias__ ("FOOCTR_B"))) foo ();
> +    void __attribute__ ((__sym_alias__ ("FOOBAR_B"))) bar () {}
> +    virtual __attribute__ ((__sym_alias__ ("FOODTR_B"))) ~foo() {}
> +  };
> +
> +  int foo::var = 2;
> +
> +  foo::foo () {
> +    void (foo::*pbar)() = &foo::bar;
> +  }
> +}
> +
> +namespace c {
> +  namespace cd {
> +    class __attribute__ ((__sym_alias__ ("FOOCLS_C"))) foo {
> +      static int var __attribute__ ((__sym_alias__ ("FOOVAR_C")));
> +      __attribute__ ((__sym_alias__ ("FOOCTR_C"))) foo () {
> +     void (foo::*pbar)() = &foo::bar;
> +      }
> +      void __attribute__ ((__sym_alias__ ("FOOBAR_C"))) bar () {}
> +      virtual __attribute__ ((__sym_alias__ ("FOODTR_C"))) ~foo() {}
> +    };
> +
> +    int foo::var = 3;
> +  }
> +}
> +
> +/* { dg-final { scan-assembler "FOOCLS_A" } } */
> +/* { dg-final { scan-assembler "FOOCLS_A_Dupe" } } */
> +/* { dg-final { scan-assembler "FOOBAR_A" } } */
> +/* { dg-final { scan-assembler "FOOCTR_A" } } */
> +/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_A" } } */
> +/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
> +/* { dg-final { scan-assembler "FOOVAR_A" } } */
> +/* { dg-final { scan-assembler "FOOCLS_B" } } */
> +/* { dg-final { scan-assembler "FOOBAR_B" } } */
> +/* { dg-final { scan-assembler "FOOCTR_B" } } */
> +/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_B" } } */
> +/* { dg-final { scan-assembler "FOODTR_B_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_B_Del" } } */
> +/* { dg-final { scan-assembler "FOOVAR_B" } } */
> +/* { dg-final { scan-assembler "FOOCLS_C" } } */
> +/* { dg-final { scan-assembler "FOOBAR_C" } } */
> +/* { dg-final { scan-assembler "FOOCTR_C" } } */
> +/* { dg-final { scan-assembler "FOOCTR_C_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_C" } } */
> +/* { dg-final { scan-assembler "FOODTR_C_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_C_Del" } } */
> +/* { dg-final { scan-assembler "FOOVAR_C" } } */
> diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C 
> b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
> new file mode 100644
> index 0000000000000..6facfcc2cd9a9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-2.C
> @@ -0,0 +1,26 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +namespace {
> +  class __attribute__ ((__sym_alias__ ("FOOCLS_A"))) foo {
> +    static int var __attribute__ ((__sym_alias__ ("FOOVAR_A")));
> +    __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
> +    virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo ();
> +    void __attribute__ ((__sym_alias__ ("FOOBAR_A"))) bar ();
> +  };
> +
> +  int foo::var = 3;
> +  foo::foo () {}
> +  foo::~foo () {}
> +  void foo::bar () {}
> +}
> +
> +/* { dg-final { scan-assembler-not "\.globl" } } */
> +/* { dg-final { scan-assembler "FOOCLS_A" } } */
> +/* { dg-final { scan-assembler "FOOBAR_A" } } */
> +/* { dg-final { scan-assembler "FOOCTR_A" } } */
> +/* { dg-final { scan-assembler "FOOCTR_A_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_A" } } */
> +/* { dg-final { scan-assembler "FOODTR_A_Base" } } */
> +/* { dg-final { scan-assembler "FOODTR_A_Del" } } */
> +/* { dg-final { scan-assembler "FOOVAR_A" } } */
> diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C 
> b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
> new file mode 100644
> index 0000000000000..42c7288ad4ad2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-3.C
> @@ -0,0 +1,83 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +// sym can be applied to template function explicit instantiations.
> +
> +template <typename T>
> +void
> +fn(T) {
> +};
> +
> +template void __attribute__ ((__sym_alias__ ("FOOFUN_UINT"))) fn<>(unsigned 
> int);
> +template void __attribute__ ((__sym_alias__ ("FOOFUN_LONG"))) fn<>(long);
> +
> +template<> void __attribute__ ((__sym_alias__ ("FOOFUN_CHAR"))) fn<>(char) {}
> +
> +
> +template <typename T = void>
> +struct
> +foo {
> +  virtual ~foo() {}
> +
> +  virtual void virtfun() {}
> +
> +  static void stfun() {}
> +  void inlfun() {}
> +};
> +
> +// Explicitly instantiate members before the enclosing class.
> +
> +template void
> +__attribute__ ((__sym_alias__ ("FOOCLS_CHAR_VIRT"))) foo<char>::virtfun();
> +
> +template class __attribute__ ((__sym_alias__ ("FOOCLS_CHAR_TI"))) foo<char>;
> +
> +// Though they're only output if the enclosing class is.
> +template void
> +__attribute__ ((__sym_alias__ ("FOOCLS_LONG_VIRT"))) foo<long>::virtfun();
> +extern
> +template class __attribute__ ((__sym_alias__ ("FOOCLS_LONG_TI_X"))) 
> foo<long>;
> +
> +
> +template void
> +__attribute__ ((__sym_alias__ ("FOOCLS_VOID_ST"))) foo<void>::stfun();
> +
> +template class __attribute__ ((__sym_alias__ ("FOOCLS_VOID_TI"))) foo<>;
> +
> +
> +extern
> +template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_X"))) 
> foo<short>;
> +
> +template void
> +__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_ST"))) foo<short>::stfun();
> +template void
> +__attribute__ ((__sym_alias__ ("FOOCLS_SHORT_INL"))) foo<short>::inlfun();
> +
> +template class __attribute__ ((__sym_alias__ ("FOOCLS_SHORT_TI_D"))) 
> foo<short>;
> +
> +// Explicit specializations work too.
> +
> +template <>
> +struct  __attribute__ ((__sym_alias__ ("FOOCLS_INT_TI")))
> +foo<int>
> +{
> +  virtual ~foo() {}
> +  virtual void __attribute__ ((__sym_alias__ ("FOOCLS_INT_VIRT"))) virtfun() 
> {}
> +};
> +
> +/* { dg-final { scan-assembler "FOOFUN_UINT" } } */
> +/* { dg-final { scan-assembler "FOOFUN_LONG" } } */
> +/* { dg-final { scan-assembler "FOOFUN_CHAR" } } */
> +
> +/* { dg-final { scan-assembler "FOOCLS_VOID_TI" } } */
> +/* { dg-final { scan-assembler "FOOCLS_VOID_ST" } } */
> +/* { dg-final { scan-assembler "FOOCLS_CHAR_TI" } } */
> +/* { dg-final { scan-assembler "FOOCLS_CHAR_VIRT" } } */
> +/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_X" } } */
> +/* { dg-final { scan-assembler "FOOCLS_SHORT_ST" } } */
> +/* { dg-final { scan-assembler "FOOCLS_SHORT_INL" } } */
> +/* { dg-final { scan-assembler "FOOCLS_SHORT_TI_D" } } */
> +/* { dg-final { scan-assembler-not "FOOCLS_LONG_TI_X" } } */
> +/* { dg-final { scan-assembler-not "FOOCLS_LONG_VIRT" } } */
> +/* { dg-final { scan-assembler "FOOCLS_INT_TI" } } */
> +/* { dg-final { scan-assembler "FOOCLS_INT_VIRT" } } */
> diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C 
> b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
> new file mode 100644
> index 0000000000000..59b779de35d80
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-4.C
> @@ -0,0 +1,28 @@
> +/* { dg-do compile } */
> +/* { dg-require-alias "" } */
> +
> +template <typename T = void>
> +class
> +__attribute__ ((__sym_alias__ ("FOOCLS")))
> +foo // { dg-error "duplicate|already" }
> +{
> +  virtual ~foo() {}
> +
> +  template <typename U>
> +  void
> +    __attribute__ ((__sym_alias__ ("FOOTMF")))
> +    tmemfun () {} // { dg-error "duplicate|already" }
> +};
> +
> +template <typename T>
> +void
> +__attribute__ ((__sym_alias__ ("FOOTFN")))
> +fn(T) { // { dg-error "duplicate|already" }
> +};
> +
> +template class foo<>;
> +template class foo<int>;
> +template void foo<>::tmemfun<void>();
> +template void foo<int>::tmemfun<void>();
> +template void fn<>(int);
> +template void fn<>(long);
> diff --git a/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C 
> b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
> new file mode 100644
> index 0000000000000..45d59b953c325
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/attr-sym-alias-5.C
> @@ -0,0 +1,14 @@
> +/* { dg-do compile { target c++11 } } */
> +/* { dg-require-alias "" } */
> +
> +struct foo {
> +  __attribute__ ((__sym_alias__ ("FOOCTR_A"))) foo ();
> +  virtual __attribute__ ((__sym_alias__ ("FOODTR_A"))) ~foo() {}
> +};
> +
> +foo::foo () {}
> +
> +// Make sure the inherited cdtors don't duplicate the syms.
> +struct bar : foo {
> +  using foo::foo;
> +};
> diff --git a/gcc/varpool.cc b/gcc/varpool.cc
> index e7b51b15e4a84..33649906d1d11 100644
> --- a/gcc/varpool.cc
> +++ b/gcc/varpool.cc
> @@ -163,6 +163,9 @@ varpool_node::get_create (tree decl)
>      }
>  
>    node->register_symbol ();
> +
> +  create_sym_alias_decls (decl);
> +
>    return node;
>  }
>  
> 
> 
> -- 
> Alexandre Oliva, happy hacker            https://FSFLA.org/blogs/lxo/
>    Free Software Activist                   GNU Toolchain Engineer
> More tolerance and less prejudice are key for inclusion and diversity
> Excluding neuro-others for not behaving ""normal"" is *not* inclusive

Reply via email to