diff mbox series

c++: block-scope externs get an alias [PR95677,PR31775,PR95677]

Message ID 85a000fc-bc4f-7fb9-ab38-5d92031f0327@acm.org
State New
Headers show
Series c++: block-scope externs get an alias [PR95677,PR31775,PR95677] | expand

Commit Message

Nathan Sidwell Oct. 7, 2020, 1:03 p.m. UTC
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.

pushing to trunk

nathan
diff mbox series

Patch

diff --git c/gcc/cp/cp-gimplify.c w/gcc/cp/cp-gimplify.c
index 07549828dc9..44c9d249b15 100644
--- c/gcc/cp/cp-gimplify.c
+++ w/gcc/cp/cp-gimplify.c
@@ -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))
diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h
index e5a2ff20223..467256117ec 100644
--- c/gcc/cp/cp-tree.h
+++ w/gcc/cp/cp-tree.h
@@ -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) \
diff --git c/gcc/cp/decl.c w/gcc/cp/decl.c
index 4ec1f4a9732..0fe74b2e851 100644
--- c/gcc/cp/decl.c
+++ w/gcc/cp/decl.c
@@ -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
 	{
diff --git c/gcc/cp/decl2.c w/gcc/cp/decl2.c
index fd48a212787..db3035dfba5 100644
--- c/gcc/cp/decl2.c
+++ w/gcc/cp/decl2.c
@@ -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)
diff --git c/gcc/cp/name-lookup.c w/gcc/cp/name-lookup.c
index ea0bfdca3d7..e3f3712b1f0 100644
--- c/gcc/cp/name-lookup.c
+++ w/gcc/cp/name-lookup.c
@@ -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
diff --git c/gcc/cp/name-lookup.h w/gcc/cp/name-lookup.h
index 01643fb9b34..d63ff108df5 100644
--- c/gcc/cp/name-lookup.h
+++ w/gcc/cp/name-lookup.h
@@ -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);
diff --git c/gcc/cp/parser.c w/gcc/cp/parser.c
index 2002c05fdb5..7a61abfa703 100644
--- c/gcc/cp/parser.c
+++ w/gcc/cp/parser.c
@@ -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
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index 1ab5435b359..3755aab092d 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -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
diff --git c/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C w/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C
index 5928894e4b9..a9acc20e87f 100644
--- c/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C
+++ w/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C
@@ -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;
 };
diff --git c/gcc/testsuite/g++.dg/init/pr42844.C w/gcc/testsuite/g++.dg/init/pr42844.C
index 9b7ed05de0e..d616f45c0a9 100644
--- c/gcc/testsuite/g++.dg/init/pr42844.C
+++ w/gcc/testsuite/g++.dg/init/pr42844.C
@@ -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> ();
diff --git c/gcc/testsuite/g++.dg/lookup/extern-redecl1.C w/gcc/testsuite/g++.dg/lookup/extern-redecl1.C
index 18e675b4f3d..f3e9b695f3b 100644
--- c/gcc/testsuite/g++.dg/lookup/extern-redecl1.C
+++ w/gcc/testsuite/g++.dg/lookup/extern-redecl1.C
@@ -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" }
 }
 
diff --git c/gcc/testsuite/g++.dg/lookup/koenig15.C w/gcc/testsuite/g++.dg/lookup/koenig15.C
index f317c010dde..6bf916a752d 100644
--- c/gcc/testsuite/g++.dg/lookup/koenig15.C
+++ w/gcc/testsuite/g++.dg/lookup/koenig15.C
@@ -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 c/gcc/testsuite/g++.dg/lto/pr95677.C w/gcc/testsuite/g++.dg/lto/pr95677.C
new file mode 100644
index 00000000000..520ef04e61c
--- /dev/null
+++ w/gcc/testsuite/g++.dg/lto/pr95677.C
@@ -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;
+}
diff --git c/gcc/testsuite/g++.dg/other/nested-extern-1.C w/gcc/testsuite/g++.dg/other/nested-extern-1.C
index 6533a2ade51..6c879e5de41 100644
--- c/gcc/testsuite/g++.dg/other/nested-extern-1.C
+++ w/gcc/testsuite/g++.dg/other/nested-extern-1.C
@@ -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 c/gcc/testsuite/g++.dg/other/nested-extern-1.cc w/gcc/testsuite/g++.dg/other/nested-extern-1.cc
new file mode 100644
index 00000000000..7d700720005
--- /dev/null
+++ w/gcc/testsuite/g++.dg/other/nested-extern-1.cc
@@ -0,0 +1,3 @@ 
+extern int i;
+
+int *p_otheri = &i;
diff --git c/gcc/testsuite/g++.dg/other/nested-extern-2.C w/gcc/testsuite/g++.dg/other/nested-extern-2.C
index 58f53e083f9..acd78ff4064 100644
--- c/gcc/testsuite/g++.dg/other/nested-extern-2.C
+++ w/gcc/testsuite/g++.dg/other/nested-extern-2.C
@@ -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 c/gcc/testsuite/g++.dg/other/nested-extern-2.cc w/gcc/testsuite/g++.dg/other/nested-extern-2.cc
new file mode 100644
index 00000000000..da43380f382
--- /dev/null
+++ w/gcc/testsuite/g++.dg/other/nested-extern-2.cc
@@ -0,0 +1,3 @@ 
+int i = 17; // never touched
+
+int *p_otheri = &i;
diff --git c/gcc/testsuite/g++.dg/other/nested-extern.cc w/gcc/testsuite/g++.dg/other/nested-extern.cc
deleted file mode 100644
index 048f715b465..00000000000
--- c/gcc/testsuite/g++.dg/other/nested-extern.cc
+++ /dev/null
@@ -1 +0,0 @@ 
-int i;
diff --git c/gcc/testsuite/g++.dg/template/scope5.C w/gcc/testsuite/g++.dg/template/scope5.C
index 629225cd556..cf23a0837bd 100644
--- c/gcc/testsuite/g++.dg/template/scope5.C
+++ w/gcc/testsuite/g++.dg/template/scope5.C
@@ -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);
 }
diff --git c/gcc/testsuite/g++.old-deja/g++.law/missed-error2.C w/gcc/testsuite/g++.old-deja/g++.law/missed-error2.C
index eaf8c01b679..7ae494b75da 100644
--- c/gcc/testsuite/g++.old-deja/g++.law/missed-error2.C
+++ w/gcc/testsuite/g++.old-deja/g++.law/missed-error2.C
@@ -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';
diff --git c/gcc/testsuite/g++.old-deja/g++.pt/crash3.C w/gcc/testsuite/g++.old-deja/g++.pt/crash3.C
index e5b3f25b530..52701b776ef 100644
--- c/gcc/testsuite/g++.old-deja/g++.pt/crash3.C
+++ w/gcc/testsuite/g++.old-deja/g++.pt/crash3.C
@@ -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" }
     }
 };