c++: block-scope externs get an alias [PR95677,PR31775,PR95677]
authorNathan Sidwell <nathan@acm.org>
Wed, 7 Oct 2020 12:46:24 +0000 (05:46 -0700)
committerNathan Sidwell <nathan@acm.org>
Wed, 7 Oct 2020 13:04:14 +0000 (06:04 -0700)
This patch improves block-scope extern handling by always injecting a
hidden copy into the enclosing namespace (or using a match already
there).  This hidden copy will be revealed if the user explicitly
declares it later.  We can get from the DECL_LOCAL_DECL_P local extern
to the alias via DECL_LOCAL_DECL_ALIAS.  This fixes several bugs and
removes the kludgy per-function extern_decl_map.  We only do this
pushing for non-dependent local externs -- dependent ones will be
pushed during instantiation.

User code that expected to be able to handle incompatible local
externs in different block-scopes will no longer work.  That code is
ill-formed.  (always was, despite what 31775 claimed).  I had to
adjust a number of testcases that fell into this.

I tried using DECL_VALUE_EXPR, but that didn't work out.  Due to
constexpr requirements we have to do the replacement very late (it
happens in the gimplifier).   Consider:

extern int l[]; // #1
constexpr bool foo ()
{
   extern int l[3]; // this does not complete the type of decl #1
   constexpr int *p = &l[2]; // ok
   return !p;
}

This requirement, coupled with our use of the common folding machinery
makes pr97306 hard to fix, as we end up with an expression containing
the two different decls for 'l', and only the c++ FE knows how to
reconcile those.  I punted on this.

gcc/cp/
* cp-tree.h (struct language_function): Delete extern_decl_map.
(DECL_LOCAL_DECL_ALIAS): New.
* name-lookup.h (is_local_extern): Delete.
* name-lookup.c (set_local_extern_decl_linkage): Replace with ...
(push_local_extern_decl): ... this new function.
(do_pushdecl): Call new function after pushing new decl.  Unhide
hidden non-functions.
(is_local_extern): Delete.
* decl.c (layout_var_decl): Do not allow VLA local externs.
* decl2.c (mark_used): Also mark DECL_LOCAL_DECL_ALIAS. Drop old
local-extern treatment.
* parser.c (cp_parser_oacc_declare): Deal with local extern aliases.
* pt.c (tsubst_expr): Adjust local extern instantiation.
* cp-gimplify.c (cp_genericize_r): Remap DECL_LOCAL_DECLs.
gcc/testsuite/
* g++.dg/cpp0x/lambda/lambda-sfinae1.C: Avoid ill-formed local extern
* g++.dg/init/pr42844.C: Add expected error.
* g++.dg/lookup/extern-redecl1.C: Likewise.
* g++.dg/lookup/koenig15.C: Avoid ill-formed.
* g++.dg/lto/pr95677.C: New.
* g++.dg/other/nested-extern-1.C: Correct expected behabviour.
* g++.dg/other/nested-extern-2.C: Likewise.
* g++.dg/other/nested-extern.cc: Split ...
* g++.dg/other/nested-extern-1.cc: ... here ...
* g++.dg/other/nested-extern-2.cc: ... here.
* g++.dg/template/scope5.C: Avoid ill-formed
* g++.old-deja/g++.law/missed-error2.C: Allow extension.
* g++.old-deja/g++.pt/crash3.C: Add expected error.

21 files changed:
gcc/cp/cp-gimplify.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/decl2.c
gcc/cp/name-lookup.c
gcc/cp/name-lookup.h
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C
gcc/testsuite/g++.dg/init/pr42844.C
gcc/testsuite/g++.dg/lookup/extern-redecl1.C
gcc/testsuite/g++.dg/lookup/koenig15.C
gcc/testsuite/g++.dg/lto/pr95677.C [new file with mode: 0644]
gcc/testsuite/g++.dg/other/nested-extern-1.C
gcc/testsuite/g++.dg/other/nested-extern-1.cc [new file with mode: 0644]
gcc/testsuite/g++.dg/other/nested-extern-2.C
gcc/testsuite/g++.dg/other/nested-extern-2.cc [new file with mode: 0644]
gcc/testsuite/g++.dg/other/nested-extern.cc [deleted file]
gcc/testsuite/g++.dg/template/scope5.C
gcc/testsuite/g++.old-deja/g++.law/missed-error2.C
gcc/testsuite/g++.old-deja/g++.pt/crash3.C

index 07549828dc94e0df37b1640d6a52183be1a76f7e..44c9d249b151c69c917c01f8db762e019160769d 100644 (file)
@@ -980,21 +980,17 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 
   /* Map block scope extern declarations to visible declarations with the
      same name and type in outer scopes if any.  */
-  if (cp_function_chain->extern_decl_map
-      && VAR_OR_FUNCTION_DECL_P (stmt)
-      && DECL_EXTERNAL (stmt))
-    {
-      struct cxx_int_tree_map *h, in;
-      in.uid = DECL_UID (stmt);
-      h = cp_function_chain->extern_decl_map->find_with_hash (&in, in.uid);
-      if (h)
-       {
-         *stmt_p = h->to;
-         TREE_USED (h->to) |= TREE_USED (stmt);
-         *walk_subtrees = 0;
-         return NULL;
-       }
-    }
+  if (VAR_OR_FUNCTION_DECL_P (stmt) && DECL_LOCAL_DECL_P (stmt))
+    if (tree alias = DECL_LOCAL_DECL_ALIAS (stmt))
+      {
+       if (alias != error_mark_node)
+         {
+           *stmt_p = alias;
+           TREE_USED (alias) |= TREE_USED (stmt);
+         }
+       *walk_subtrees = 0;
+       return NULL;
+      }
 
   if (TREE_CODE (stmt) == INTEGER_CST
       && TYPE_REF_P (TREE_TYPE (stmt))
index e5a2ff20223c1e6b6fcc92978b5e8bc606556620..467256117ec1ddf0eb8470471bae64a6413d564b 100644 (file)
@@ -1926,7 +1926,6 @@ struct GTY(()) language_function {
   /* Tracking possibly infinite loops.  This is a vec<tree> only because
      vec<bool> doesn't work with gtype.  */
   vec<tree, va_gc> *infinite_loops;
-  hash_table<cxx_int_tree_map_hasher> *extern_decl_map;
 };
 
 /* The current C++-specific per-function global variables.  */
@@ -2697,6 +2696,7 @@ struct GTY(()) lang_decl_min {
      In a lambda-capture proxy VAR_DECL, this is DECL_CAPTURED_VARIABLE.
      In a function-scope TREE_STATIC VAR_DECL or IMPLICIT_TYPEDEF_P TYPE_DECL,
      this is DECL_DISCRIMINATOR.
+     In a DECL_LOCAL_DECL_P decl, this is the namespace decl it aliases.
      Otherwise, in a class-scope DECL, this is DECL_ACCESS.   */
   tree access;
 };
@@ -4023,6 +4023,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_LOCAL_DECL_P(NODE) \
   DECL_LANG_FLAG_0 (VAR_OR_FUNCTION_DECL_CHECK (NODE))
 
+/* The namespace-scope decl a DECL_LOCAL_DECL_P aliases.  */
+#define DECL_LOCAL_DECL_ALIAS(NODE)                    \
+  DECL_ACCESS ((gcc_checking_assert (DECL_LOCAL_DECL_P (NODE)), NODE))
+
 /* Nonzero if NODE is the target for genericization of 'return' stmts
    in constructors/destructors of targetm.cxx.cdtor_returns_this targets.  */
 #define LABEL_DECL_CDTOR(NODE) \
index 4ec1f4a973238741a39d44ed32e8f7472e0c53cd..0fe74b2e851db202b0b6341381eb7d8383dfef10 100644 (file)
@@ -5830,7 +5830,8 @@ layout_var_decl (tree decl)
       && DECL_SIZE (decl) != NULL_TREE
       && ! TREE_CONSTANT (DECL_SIZE (decl)))
     {
-      if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST)
+      if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST
+         && !DECL_LOCAL_DECL_P (decl))
        constant_expression_warning (DECL_SIZE (decl));
       else
        {
index fd48a212787c7579dcff8d1bbbf02cb65d1bc287..db3035dfba5f759522bbb7fe91741d94bb9079b7 100644 (file)
@@ -5567,6 +5567,22 @@ mark_used (tree decl, tsubst_flags_t complain)
       return false;
     }
 
+  if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_LOCAL_DECL_P (decl))
+    {
+      if (!DECL_LANG_SPECIFIC (decl))
+       /* An unresolved dependent local extern.  */
+       return true;
+
+      DECL_ODR_USED (decl) = 1;
+      auto alias = DECL_LOCAL_DECL_ALIAS (decl);
+      if (!alias || alias == error_mark_node)
+       return true;
+
+      /* Process the underlying decl.  */
+      decl = alias;
+      TREE_USED (decl) = true;
+    }
+
   cp_warn_deprecated_use (decl, complain);
 
   /* We can only check DECL_ODR_USED on variables or functions with
@@ -5650,14 +5666,7 @@ mark_used (tree decl, tsubst_flags_t complain)
       && !DECL_ARTIFICIAL (decl)
       && !decl_defined_p (decl)
       && no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false))
-    {
-      if (is_local_extern (decl))
-       /* There's no way to define a local extern, and adding it to
-          the vector interferes with GC, so give an error now.  */
-       no_linkage_error (decl);
-      else
-       vec_safe_push (no_linkage_decls, decl);
-    }
+    vec_safe_push (no_linkage_decls, decl);
 
   if (TREE_CODE (decl) == FUNCTION_DECL
       && DECL_DECLARED_INLINE_P (decl)
index ea0bfdca3d7181fdf6f4c57374a45e2e5432960a..e3f3712b1f0631ebf5128bfeab8e0e77c2f31301 100644 (file)
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 
 static cxx_binding *cxx_binding_make (tree value, tree type);
 static cp_binding_level *innermost_nonclass_level (void);
+static tree do_pushdecl_with_scope (tree x, cp_binding_level *, bool hiding);
 static void set_identifier_type_value_with_scope (tree id, tree decl,
                                                  cp_binding_level *b);
 static name_hint maybe_suggest_missing_std_header (location_t location,
@@ -2921,108 +2922,66 @@ set_decl_context_in_fn (tree ctx, tree decl)
     DECL_CONTEXT (decl) = ctx;
 }
 
-/* DECL is a local-scope decl with linkage.  SHADOWED is true if the
-   name is already bound at the current level.
-
-   [basic.link] If there is a visible declaration of an entity with
-   linkage having the same name and type, ignoring entities declared
-   outside the innermost enclosing namespace scope, the block scope
-   declaration declares that same entity and receives the linkage of
-   the previous declaration.
-
-   Also, make sure that this decl matches any existing external decl
-   in the enclosing namespace.  */
+/* DECL is a local extern decl.  Find or create the namespace-scope
+   decl that it aliases.  Also, determines the linkage of DECL.  */
 
 static void
-set_local_extern_decl_linkage (tree decl, bool shadowed)
+push_local_extern_decl_alias (tree decl)
 {
-  tree ns_value = decl; /* Unique marker.  */
-
-  if (!shadowed)
-    {
-      tree loc_value = innermost_non_namespace_value (DECL_NAME (decl));
-      if (!loc_value)
-       {
-         ns_value
-           = find_namespace_value (current_namespace, DECL_NAME (decl));
-         loc_value = ns_value;
-       }
-      if (loc_value == error_mark_node
-         /* An ambiguous lookup.  */
-         || (loc_value && TREE_CODE (loc_value) == TREE_LIST))
-       loc_value = NULL_TREE;
-
-      for (ovl_iterator iter (loc_value); iter; ++iter)
-       if (!iter.hidden_p ()
-           && (TREE_STATIC (*iter) || DECL_EXTERNAL (*iter))
-           && decls_match (*iter, decl))
-         {
-           /* The standard only says that the local extern inherits
-              linkage from the previous decl; in particular, default
-              args are not shared.  Add the decl into a hash table to
-              make sure only the previous decl in this case is seen
-              by the middle end.  */
-           struct cxx_int_tree_map *h;
-
-           /* We inherit the outer decl's linkage.  But we're a
-              different decl.  */
-           TREE_PUBLIC (decl) = TREE_PUBLIC (*iter);
-
-           if (cp_function_chain->extern_decl_map == NULL)
-             cp_function_chain->extern_decl_map
-               = hash_table<cxx_int_tree_map_hasher>::create_ggc (20);
-
-           h = ggc_alloc<cxx_int_tree_map> ();
-           h->uid = DECL_UID (decl);
-           h->to = *iter;
-           cxx_int_tree_map **loc = cp_function_chain->extern_decl_map
-             ->find_slot (h, INSERT);
-           *loc = h;
-           break;
-         }
-    }
+  if (dependent_type_p (TREE_TYPE (decl)))
+    return;
+  /* EH specs were not part of the function type prior to c++17, but
+     we still can't go pushing dependent eh specs into the namespace.  */
+  if (cxx_dialect < cxx17
+      && TREE_CODE (decl) == FUNCTION_DECL
+      && (value_dependent_expression_p
+         (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)))))
+    return;
 
-  if (TREE_PUBLIC (decl))
-    {
-      /* DECL is externally visible.  Make sure it matches a matching
-        decl in the namespace scope.  We only really need to check
-        this when inserting the decl, not when we find an existing
-        match in the current scope.  However, in practice we're
-        going to be inserting a new decl in the majority of cases --
-        who writes multiple extern decls for the same thing in the
-        same local scope?  Doing it here often avoids a duplicate
-        namespace lookup.  */
+  gcc_checking_assert (!DECL_LANG_SPECIFIC (decl)
+                      || !DECL_TEMPLATE_INFO (decl));
+  if (DECL_LANG_SPECIFIC (decl) && DECL_LOCAL_DECL_ALIAS (decl))
+    /* We're instantiating a non-dependent local decl, it already
+       knows the alias.  */
+    return;
 
-      /* Avoid repeating a lookup.  */
-      if (ns_value == decl)
-       ns_value = find_namespace_value (current_namespace, DECL_NAME (decl));
+  tree alias = NULL_TREE;
 
-      if (ns_value == error_mark_node
-         || (ns_value && TREE_CODE (ns_value) == TREE_LIST))
-       ns_value = NULL_TREE;
+  if (DECL_SIZE (decl) && !TREE_CONSTANT (DECL_SIZE (decl)))
+    /* Do not let a VLA creep into a namespace.  Diagnostic will be
+       emitted in layout_var_decl later.  */
+    alias = error_mark_node;
+  else
+    {
+      /* First look for a decl that matches.  */
+      tree ns = CP_DECL_CONTEXT (decl);
+      tree binding = find_namespace_value (ns, DECL_NAME (decl));
 
-      for (ovl_iterator iter (ns_value); iter; ++iter)
-       {
-         tree other = *iter;
-
-         if (!(TREE_PUBLIC (other) || DECL_EXTERNAL (other)))
-           ; /* Not externally visible.   */
-         else if (DECL_EXTERN_C_P (decl) && DECL_EXTERN_C_P (other))
-           ; /* Both are extern "C", we'll check via that mechanism.  */
-         else if (TREE_CODE (other) != TREE_CODE (decl)
-                  || ((VAR_P (decl) || matching_fn_p (other, decl))
-                      && !comptypes (TREE_TYPE (decl), TREE_TYPE (other),
-                                     COMPARE_REDECLARATION)))
+      if (binding && TREE_CODE (binding) != TREE_LIST)
+       for (ovl_iterator iter (binding); iter; ++iter)
+         if (decls_match (*iter, decl))
            {
-             auto_diagnostic_group d;
-             if (permerror (DECL_SOURCE_LOCATION (decl),
-                            "local external declaration %q#D", decl))
-               inform (DECL_SOURCE_LOCATION (other),
-                       "does not match previous declaration %q#D", other);
+             alias = *iter;
              break;
            }
+
+      if (!alias)
+       {
+         /* No existing namespace-scope decl.  Make one.  */
+         alias = copy_decl (decl);
+
+         /* This is the real thing.  */
+         DECL_LOCAL_DECL_P (alias) = false;
+
+         /* Expected default linkage is from the namespace.  */
+         TREE_PUBLIC (alias) = TREE_PUBLIC (ns);
+         alias = do_pushdecl_with_scope (alias, NAMESPACE_LEVEL (ns),
+                                         /* hiding= */true);
        }
     }
+
+  retrofit_lang_decl (decl);
+  DECL_LOCAL_DECL_ALIAS (decl) = alias;
 }
 
 /* Record DECL as belonging to the current lexical scope.  Check for
@@ -3080,10 +3039,6 @@ do_pushdecl (tree decl, bool hiding)
            old = binding->value;
        }
 
-      if (current_function_decl && VAR_OR_FUNCTION_DECL_P (decl)
-         && DECL_EXTERNAL (decl))
-       set_local_extern_decl_linkage (decl, old != NULL_TREE);
-
       if (old == error_mark_node)
        old = NULL_TREE;
 
@@ -3115,6 +3070,16 @@ do_pushdecl (tree decl, bool hiding)
                  /* We need to check and register the decl now.  */
                  check_extern_c_conflict (match);
              }
+           else if (slot && !hiding
+                    && STAT_HACK_P (*slot) && STAT_DECL_HIDDEN_P (*slot))
+             {
+               /* Unhide the non-function.  */
+               gcc_checking_assert (old == match);
+               if (!STAT_TYPE (*slot))
+                 *slot = match;
+               else
+                 STAT_DECL (*slot) = match;
+             }
            return match;
          }
 
@@ -3190,12 +3155,21 @@ do_pushdecl (tree decl, bool hiding)
          if (!instantiating_current_function_p ())
            record_locally_defined_typedef (decl);
        }
-      else if (VAR_P (decl))
-       maybe_register_incomplete_var (decl);
+      else
+       {
+         if (VAR_P (decl) && !DECL_LOCAL_DECL_P (decl))
+           maybe_register_incomplete_var (decl);
+
+         if (VAR_OR_FUNCTION_DECL_P (decl))
+           {
+             if (DECL_LOCAL_DECL_P (decl)
+                 && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL)
+               push_local_extern_decl_alias (decl);
 
-      if ((VAR_P (decl) || TREE_CODE (decl) == FUNCTION_DECL)
-         && DECL_EXTERN_C_P (decl))
-       check_extern_c_conflict (decl);
+             if (DECL_EXTERN_C_P (decl))
+               check_extern_c_conflict (decl);
+           }
+       }
     }
   else
     add_decl_to_level (level, decl);
@@ -6871,20 +6845,6 @@ lookup_elaborated_type (tree name, TAG_how how)
   return ret;
 }
 
-/* Returns true iff DECL is a block-scope extern declaration of a function
-   or variable.  We will already have determined validity of the decl
-   when pushing it.  So we do not have to redo that lookup.  */
-
-bool
-is_local_extern (tree decl)
-{
-  if ((TREE_CODE (decl) == FUNCTION_DECL
-       || TREE_CODE (decl) == VAR_DECL))
-    return DECL_LOCAL_DECL_P (decl);
-
-  return false;
-}
-
 /* The type TYPE is being declared.  If it is a class template, or a
    specialization of a class template, do any processing required and
    perform error-checking.  If IS_FRIEND is nonzero, this TYPE is
index 01643fb9b340eace7b8e7835ac45918d08dd0a81..d63ff108df513ec9a6f68391097d7747ef231e85 100644 (file)
@@ -342,7 +342,6 @@ extern tree lookup_qualified_name (tree scope, tree name,
 extern tree lookup_qualified_name (tree scope, const char *name,
                                   LOOK_want = LOOK_want::NORMAL,
                                   bool = true);
-extern bool is_local_extern (tree);
 extern bool pushdecl_class_level (tree);
 extern tree pushdecl_namespace_level (tree, bool hiding = false);
 extern bool push_class_level_binding (tree, tree);
index 2002c05fdb5e328e4e08785bd3d608058837d133..7a61abfa7038e3689884ec7d00e8fb2571ad1a13 100644 (file)
@@ -41176,6 +41176,10 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
        }
 
       if (!found_in_scope)
+       /* This seems to ignore the existence of cleanup scopes?
+          What is the meaning for local extern decls?  The local
+          extern is in this scope, but it is referring to a decl that
+          is namespace scope.  */
        for (tree d = current_binding_level->names; d; d = TREE_CHAIN (d))
          if (d == decl)
            {
@@ -41205,6 +41209,16 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
        {
          tree id;
 
+         if (DECL_LOCAL_DECL_P (decl))
+           /* We need to mark the aliased decl, as that is the entity
+              that is being referred to.  This won't work for
+              dependent variables, but it didn't work for them before
+              DECL_LOCAL_DECL_P was a thing either.  But then
+              dependent local extern variable decls are as rare as
+              hen's teeth.  */
+           if (auto alias = DECL_LOCAL_DECL_ALIAS (decl))
+             decl = alias;
+
          if (OMP_CLAUSE_MAP_KIND (t) == GOMP_MAP_LINK)
            id = get_identifier ("omp declare target link");
          else
index 1ab5435b35975d3cf613123e48da28de09a0c63e..3755aab092d4f2ba925521b7e5310e88d41bb509 100644 (file)
@@ -18104,13 +18104,15 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
                  }
                else if (DECL_IMPLICIT_TYPEDEF_P (t))
                  /* We already did a pushtag.  */;
-               else if (TREE_CODE (decl) == FUNCTION_DECL
-                        && DECL_LOCAL_DECL_P (decl)
-                        && DECL_OMP_DECLARE_REDUCTION_P (decl))
+               else if (VAR_OR_FUNCTION_DECL_P (decl)
+                        && DECL_LOCAL_DECL_P (decl))
                  {
-                   DECL_CONTEXT (decl) = current_function_decl;
-                   pushdecl (decl);
-                   if (cp_check_omp_declare_reduction (decl))
+                   if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
+                     DECL_CONTEXT (decl) = NULL_TREE;
+                   decl = pushdecl (decl);
+                   if (TREE_CODE (decl) == FUNCTION_DECL
+                       && DECL_OMP_DECLARE_REDUCTION_P (decl)
+                       && cp_check_omp_declare_reduction (decl))
                      instantiate_body (pattern_decl, args, decl, true);
                  }
                else
index 5928894e4b9ccd94019f9ee5fb86af810c13062e..a9acc20e87f540e73d97670d788bfe20fddb8101 100644 (file)
@@ -9,7 +9,7 @@ struct AddRvalueReferenceImpl { typedef T type; };
 template <typename T>
 struct AddRvalueReferenceImpl<T, typename BoolSink<false &&
       [] {                     // { dg-error "lambda" "" { target c++17_down } }
-         extern T &&tref;
+       extern void tref(T&&);
       }>::type> {
    typedef T &&type;
 };
index 9b7ed05de0e3c87e4d91adebe503994c86faae22..d616f45c0a914e66d90ad5cc93538fb5a039c0cd 100644 (file)
@@ -49,7 +49,7 @@ template <class T>
 void g ()
 {
   T const t; // { dg-error "uninitialized 'const" }
-  extern T const text;
+  extern T const text; // { dg-error "conflicting declaration" }
 }
 
 template void g<H> ();
index 18e675b4f3da15ee2b0a19ae80224f67680bf3fb..f3e9b695f3b5aa68d65522455c09dc984bee0ab6 100644 (file)
@@ -1,18 +1,18 @@
 extern int X; // { dg-message "previous declaration" }
-extern int Y (int);  // { dg-message "previous declaration" }
+extern int Y (int);  // { dg-message "old declaration" }
 extern int Y (float);
 
-static int Z (int s)
+static int Z (int s) // { dg-message "old declaration" }
 {
   return s;
 }
 
 void Foo ()
 {
-  extern char X; // { dg-error "local external declaration" }
-  extern char Y (int); // { dg-error "local external declaration" }
+  extern char X; // { dg-error "conflicting declaration" }
+  extern char Y (int); // { dg-error "ambiguating new declaration" }
   extern int Y (float);
   extern void Y (double);
-  extern char Z (int);
+  extern char Z (int); // { dg-error "ambiguating new declaration" }
 }
 
index f317c010dde196ca7b3a14c3490f376f238aa298..6bf916a752d69e4a41e8ab4878eeca76859678eb 100644 (file)
@@ -4,10 +4,12 @@
 namespace N {
   struct S { };
   void f(S);
+  void g(S);
 }
 
 namespace M {
   void f(int);
+  void g(int);
 }
 
 void
@@ -40,6 +42,6 @@ void
 fn3 ()
 {
   N::S s;
-  extern void (*f)(char);
-  f (s); // { dg-error "cannot convert" }
+  extern void (*g)(char);
+  g (s); // { dg-error "cannot convert" }
 }
diff --git a/gcc/testsuite/g++.dg/lto/pr95677.C b/gcc/testsuite/g++.dg/lto/pr95677.C
new file mode 100644 (file)
index 0000000..520ef04
--- /dev/null
@@ -0,0 +1,19 @@
+// PR c++/95677
+
+// { dg-do link }
+// { dg-require-effective-target lto }
+// { dg-options "-flto" }
+
+
+
+namespace {
+  void foo() {
+    extern int xx; // injects a *static*
+    xx = 0;
+  }
+  int xx = 1;
+}
+
+int main() {
+  xx = 2;
+}
index 6533a2ade51a29f93f3ac8d5303613a8636c1fe9..6c879e5de413ea8151b5d0610591dbeb382aef2d 100644 (file)
@@ -1,17 +1,21 @@
 /* { dg-do run } */
-// { dg-additional-sources "nested-extern.cc" }
+// { dg-additional-options "-fpermissive" }
+// { dg-additional-sources "nested-extern-1.cc" }
 /* PR 31775 */
-extern "C" void abort();
+extern int *p_otheri;
 extern int *p;
 int main()
 { 
-  extern int i;
+  extern int i; // { dg-message "previous declaration" }
   i = 1;
   *p = 2;
-  if (i == 2)
-    abort ();
+  if (i != 2)
+    return 1;
+  if (p_otheri != p)
+    return 2;
   return 0;
 }
 
-static int i;
+// This is extern because of the injection above.
+static int i; // { dg-warning ".extern. and later .static" }
 int *p = &i;
diff --git a/gcc/testsuite/g++.dg/other/nested-extern-1.cc b/gcc/testsuite/g++.dg/other/nested-extern-1.cc
new file mode 100644 (file)
index 0000000..7d70072
--- /dev/null
@@ -0,0 +1,3 @@
+extern int i;
+
+int *p_otheri = &i;
index 58f53e083f9decd7325a51ab5c455d5f3c4246fd..acd78ff406421bf844de8574806edf1ba02e0063 100644 (file)
@@ -1,18 +1,27 @@
 /* { dg-do run } */
-// { dg-additional-sources "nested-extern.cc" }
 /* PR 31775 */
-extern "C" void abort();
-static int i;
-int *p = &i;
+// { dg-additional-sources "nested-extern-2.cc" }
+extern int *p_otheri;
+static int i; // #1
+int *p_si = &i;
 int main()
 { 
   int i;
-  { 
+  int *p_ai = &i;
+  {
+    // This is an alias of #1, not a different object
     extern int i;
-    i = 1;
-    *p = 2;
-    if (i == 2)
-      abort ();
+    int *p_ei = &i;
+
+    *p_si = 1;
+    *p_ai = 2;
+    *p_ei = 3;
+    if (*p_si != 3)
+      return 1;
+    if (*p_ai != 2)
+      return 2;
+    if (*p_otheri != 17)
+      return 3;
   }
   return 0;
 }
diff --git a/gcc/testsuite/g++.dg/other/nested-extern-2.cc b/gcc/testsuite/g++.dg/other/nested-extern-2.cc
new file mode 100644 (file)
index 0000000..da43380
--- /dev/null
@@ -0,0 +1,3 @@
+int i = 17; // never touched
+
+int *p_otheri = &i;
diff --git a/gcc/testsuite/g++.dg/other/nested-extern.cc b/gcc/testsuite/g++.dg/other/nested-extern.cc
deleted file mode 100644 (file)
index 048f715..0000000
+++ /dev/null
@@ -1 +0,0 @@
-int i;
index 629225cd556772bdd1c725257aa00f52e881fc56..cf23a0837bd230fe13d876a2a13c7425e6e8ce29 100644 (file)
@@ -57,10 +57,12 @@ enum { a = b::e<0>::f };
 template <typename> class au;
 template <typename av> struct ac : ao<av> { typedef c::e<am::an> aq; };
 template <typename aw, typename i, typename ax> void ay(aw, i, ax) {
-  au<c::e<ap<typename ak<i>::o>::f> > az();
+  // Not sure if this has been creduced from an initialization of a
+  // variable to a block-scope extern function decl
+  au<c::e<ap<typename ak<i>::o>::f> > az2();
 }
 void v() {
   ad a;
-  void az();
-  ay(az, a, v);
+  void az1();
+  ay(az1, a, v);
 }
index eaf8c01b679624fd57a6fa95847c6b7b1ea9dde7..7ae494b75da09ea9ada970845388db36971ef334 100644 (file)
@@ -1,4 +1,5 @@
 // { dg-do assemble  }
+// { dg-additional-options -fpermissive }
 // GROUPS passed missed-error
 // missed-error file
 // From: ndc!don@csvax.cs.caltech.edu (Don Erway)
@@ -20,14 +21,14 @@ inline double max(double a, double b) {return a > b ? a : b;}; // { dg-message "
  // { dg-error "extra ';'" "extra ;" { target c++98_only } .-1 }
 
 int main() {
+  // we treat this as-if extern
    static void foo(int i, int j, double x, double y) ;// { dg-error "" } .*
 
    foo(4, -37, 14.39, 14.38);
 }
 
-// 971006 we no longer give an error for this since we emit a hard error
-// about the declaration above
-static void foo(int i, int j, double x, double y) { 
+static void foo(int i, int j, double x, double y) // { dg-warning ".extern." }
+{ 
 
    std::cout << "Max(int): " << max(i,j) << " Max(double): " <<
 max(x,y) << '\n';
index e5b3f25b5303b15e65bf08e00234dc50d473365d..52701b776ef484c85f2a0a4ffa6273b20c12ff92 100644 (file)
@@ -5,12 +5,13 @@ class CVector {
 public:
     CVector<int> f() const
     {
-       CVector<int> v();
+      // local-extern :)
+      CVector<int> v(); // { dg-message "old declaration" }
        return v;               // { dg-error "convert" }
     }
     CVector<long> g() const
     {
-       CVector<long> v();
+      CVector<long> v(); // { dg-error "ambiguating new" }
        return v;               // { dg-error "convert" }
     }
 };