This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH to handling of types with no linkage (DR 757)


Over the years there have been various tweaks to restrictions on the use of types with no linkage. The original rule was that you can't use a type with no linkage to declare a variable or function with linkage, and therefore can't use one as a template argument. The latest revision, from DR 757, says that doing this is fine as long as the variable or function is either not used (in the [basic.def.odr] sense) or defined in the same translation unit. This seems like a good solution to me, and this patch implements it.

As noted in the ??? comment above DECL_NO_LINKAGE_CHECKED, I'm not very happy about adding another flag when we ought to be able to just use TREE_USED to return early from mark_used. But that's not how it works currently, and I'm not sure what complications might come up when trying to change that. I guess we should probably add C_DECL_USED in the C++ front end as well, I'll look at doing that soon.

Tested x86_64-pc-linux-gnu, applied to trunk.
2009-08-10  Jason Merrill  <jason@redhat.com>

	Implement DR 757: It's OK for a decl to use a type without linkage
	so long as the decl is defined in the current translation unit.
	* decl2.c (no_linkage_decls): New vector.
	(mark_used): Add decls that use types with no linkage.
	(cp_write_global_declarations): Check that they are defined.
	(decl_defined_p, no_linkage_error): New fns.
	* cp-tree.h (DECL_NO_LINKAGE_CHECKED): New macro.
	(struct lang_decl_base): Add flag.
	* decl.c (grokfndecl): Don't check type linkage.
	(grokvardecl): If the type has no linkage, just make sure
	DECL_LANG_SPECIFIC is set.
	* pt.c (check_instantiated_arg): Don't check type linkage.
	* name-lookup.c (is_local_extern): New fn.
	* name-lookup.h: Declare it.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index c507ac8..ae39110 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1579,8 +1579,9 @@ struct GTY(()) lang_decl_base {
   unsigned anticipated_p : 1;		   /* fn or type */
   unsigned friend_attr : 1;		   /* fn or type */
   unsigned template_conv_p : 1;		   /* template only? */
+  unsigned no_linkage_checked : 1;         /* var or fn */
   unsigned u2sel : 1;
-  /* 2 spare bits */
+  /* 1 spare bit */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -1982,6 +1983,14 @@ struct GTY(()) lang_decl {
   (DECL_LANG_SPECIFIC (VAR_OR_FUNCTION_DECL_CHECK (DECL)) \
    ->u.base.initialized_in_class)
 
+/* Nonzero if we've checked whether DECL uses types without linkage in a
+   potentially invalid way.
+   ??? Instead, should fix mark_used to only set TREE_USED when we're
+   really using something, and just return if it's already set.  */
+#define DECL_NO_LINKAGE_CHECKED(DECL) \
+  (DECL_LANG_SPECIFIC (VAR_OR_FUNCTION_DECL_CHECK (DECL)) \
+   ->u.base.no_linkage_checked)
+
 /* Nonzero for DECL means that this decl is just a friend declaration,
    and should not be added to the list of members for this class.  */
 #define DECL_FRIEND_P(NODE) (DECL_LANG_SPECIFIC (NODE)->u.base.friend_attr)
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index be1b5b7..898542f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6747,36 +6747,6 @@ grokfndecl (tree ctype,
 		|| decl_function_context (TYPE_MAIN_DECL (ctype))))
     publicp = 0;
 
-  if (publicp)
-    {
-      /* [basic.link]: A name with no linkage (notably, the name of a class
-	 or enumeration declared in a local scope) shall not be used to
-	 declare an entity with linkage.
-
-	 Only check this for public decls for now.  See core 319, 389.  */
-      t = no_linkage_check (TREE_TYPE (decl),
-			    /*relaxed_p=*/false);
-      if (t)
-	{
-	  if (TYPE_ANONYMOUS_P (t))
-	    {
-	      if (DECL_EXTERN_C_P (decl))
-		/* Allow this; it's pretty common in C.  */;
-	      else
-		{
-		  permerror (input_location, "non-local function %q#D uses anonymous type",
-			      decl);
-		  if (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))
-		    permerror (input_location, "%q+#D does not refer to the unqualified "
-			       "type, so it is not used for linkage",
-			       TYPE_NAME (t));
-		}
-	    }
-	  else
-	    permerror (input_location, "non-local function %q#D uses local type %qT", decl, t);
-	}
-    }
-
   TREE_PUBLIC (decl) = publicp;
   if (! publicp)
     {
@@ -7021,36 +6991,13 @@ grokvardecl (tree type,
 
   if (TREE_PUBLIC (decl))
     {
-      /* [basic.link]: A name with no linkage (notably, the name of a class
-	 or enumeration declared in a local scope) shall not be used to
-	 declare an entity with linkage.
-
-	 Only check this for public decls for now.  */
-      tree t = no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false);
-      if (t)
-	{
-	  if (TYPE_ANONYMOUS_P (t))
-	    {
-	      if (DECL_EXTERN_C_P (decl))
-		/* Allow this; it's pretty common in C.  */
-		  ;
-	      else
-		{
-		  /* DRs 132, 319 and 389 seem to indicate types with
-		     no linkage can only be used to declare extern "C"
-		     entities.  Since it's not always an error in the
-		     ISO C++ 90 Standard, we only issue a warning.  */
-		  warning (0, "non-local variable %q#D uses anonymous type",
-			   decl);
-		  if (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))
-		    warning (0, "%q+#D does not refer to the unqualified "
-			     "type, so it is not used for linkage",
-			     TYPE_NAME (t));
-		}
-	    }
-	  else
-	    warning (0, "non-local variable %q#D uses local type %qT", decl, t);
-	}
+      /* If the type of the decl has no linkage, make sure that we'll
+	 notice that in mark_used.  */
+      if (DECL_LANG_SPECIFIC (decl) == NULL
+	  && TREE_PUBLIC (decl)
+	  && !DECL_EXTERN_C_P (decl)
+	  && no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false))
+	retrofit_lang_decl (decl);
     }
   else
     DECL_INTERFACE_KNOWN (decl) = 1;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index df79e9c..610d62d 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -84,6 +84,7 @@ static void write_out_vars (tree);
 static void import_export_class (tree);
 static tree get_guard_bits (tree);
 static void determine_visibility_from_class (tree, tree);
+static bool decl_defined_p (tree);
 
 /* A list of static class variables.  This is needed, because a
    static class variable can be declared inside the class without
@@ -94,6 +95,10 @@ static GTY(()) VEC(tree,gc) *pending_statics;
    may need to emit outline anyway.  */
 static GTY(()) VEC(tree,gc) *deferred_fns;
 
+/* A list of decls that use types with no linkage, which we need to make
+   sure are defined.  */
+static GTY(()) VEC(tree,gc) *no_linkage_decls;
+
 /* Nonzero if we're done parsing and into end-of-file activities.  */
 
 int at_eof;
@@ -3332,6 +3337,40 @@ build_java_method_aliases (void)
     }
 }
 
+/* Returns true iff there is a definition available for variable or
+   function DECL.  */
+
+static bool
+decl_defined_p (tree decl)
+{
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    return (DECL_INITIAL (decl) != NULL_TREE);
+  else
+    {
+      gcc_assert (TREE_CODE (decl) == VAR_DECL);
+      return !DECL_EXTERNAL (decl);
+    }
+}
+
+/* Complain that DECL uses a type with no linkage but is never defined.  */
+
+static void
+no_linkage_error (tree decl)
+{
+  tree t = no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false);
+  if (TYPE_ANONYMOUS_P (t))
+    {
+      permerror (0, "%q+#D, declared using anonymous type, "
+		 "is used but never defined", decl);
+      if (is_typedef_decl (TYPE_NAME (t)))
+	permerror (0, "%q+#D does not refer to the unqualified type, "
+		   "so it is not used for linkage", TYPE_NAME (t));
+    }
+  else
+    permerror (0, "%q+#D, declared using local type %qT, "
+	       "is used but never defined", decl, t);
+}
+
 /* This routine is called at the end of compilation.
    Its job is to create all the code needed to initialize and
    destroy the global aggregates.  We do the destruction
@@ -3613,6 +3652,11 @@ cp_write_global_declarations (void)
 	}
     }
 
+  /* So must decls that use a type with no linkage.  */
+  for (i = 0; VEC_iterate (tree, no_linkage_decls, i, decl); ++i)
+    if (!decl_defined_p (decl))
+      no_linkage_error (decl);
+
   /* We give C linkage to static constructors and destructors.  */
   push_lang_context (lang_name_c);
 
@@ -3851,6 +3895,32 @@ mark_used (tree decl)
   if (processing_template_decl)
     return;
 
+  /* DR 757: A type without linkage shall not be used as the type of a
+     variable or function with linkage, unless
+   o the variable or function has extern "C" linkage (7.5 [dcl.link]), or
+   o the variable or function is not used (3.2 [basic.def.odr]) or is
+   defined in the same translation unit.  */
+  if (TREE_PUBLIC (decl)
+      && (TREE_CODE (decl) == FUNCTION_DECL
+	  || TREE_CODE (decl) == VAR_DECL)
+      && DECL_LANG_SPECIFIC (decl)
+      && !DECL_NO_LINKAGE_CHECKED (decl))
+    {
+      DECL_NO_LINKAGE_CHECKED (decl) = true;
+      if (!DECL_EXTERN_C_P (decl)
+	  && !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 (tree, gc, no_linkage_decls, decl);
+	}
+    }
+
   if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl)
       && !TREE_ASM_WRITTEN (decl))
     /* Remember it, so we can check it was defined.  */
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index c2d8779..feb2cf2 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4392,6 +4392,34 @@ lookup_name_innermost_nonclass_level (tree name)
   POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
 }
 
+/* Returns true iff DECL is a block-scope extern declaration of a function
+   or variable.  */
+
+bool
+is_local_extern (tree decl)
+{
+  cxx_binding *binding;
+
+  /* For functions, this is easy.  */
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    return DECL_LOCAL_FUNCTION_P (decl);
+
+  if (TREE_CODE (decl) != VAR_DECL)
+    return false;
+  if (!current_function_decl)
+    return false;
+
+  /* For variables, this is not easy.  We need to look at the binding stack
+     for the identifier to see whether the decl we have is a local.  */
+  for (binding = IDENTIFIER_BINDING (DECL_NAME (decl));
+       binding && binding->scope->kind != sk_namespace;
+       binding = binding->previous)
+    if (binding->value == decl)
+      return LOCAL_BINDING_P (binding);
+
+  return false;
+}
+
 /* Like lookup_name_innermost_nonclass_level, but for types.  */
 
 static tree
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 2203a84..7a3625a 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -318,6 +318,7 @@ extern tree remove_hidden_names (tree);
 extern tree lookup_qualified_name (tree, tree, bool, bool);
 extern tree lookup_name_nonclass (tree);
 extern tree lookup_name_innermost_nonclass_level (tree);
+extern bool is_local_extern (tree);
 extern tree lookup_function_nonclass (tree, VEC(tree,gc) *, bool);
 extern void push_local_binding (tree, tree, int);
 extern bool pushdecl_class_level (tree);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c0c61c5..36f1b00 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12215,7 +12215,7 @@ tsubst_copy_and_build (tree t,
 }
 
 /* Verify that the instantiated ARGS are valid. For type arguments,
-   make sure that the type's linkage is ok. For non-type arguments,
+   make sure that the type is not variably modified. For non-type arguments,
    make sure they are constants if they are integral or enumerations.
    Emit an error under control of COMPLAIN, and return TRUE on error.  */
 
@@ -12236,30 +12236,7 @@ check_instantiated_arg (tree tmpl, tree t, tsubst_flags_t complain)
     }
   else if (TYPE_P (t))
     {
-      /* [basic.link]: A name with no linkage (notably, the name
-	 of a class or enumeration declared in a local scope)
-	 shall not be used to declare an entity with linkage.
-	 This implies that names with no linkage cannot be used as
-	 template arguments.  */
-      tree nt = no_linkage_check (t, /*relaxed_p=*/false);
-
-      if (nt)
-	{
-	  /* DR 488 makes use of a type with no linkage cause
-	     type deduction to fail.  */
-	  if (complain & tf_error)
-	    {
-	      if (TYPE_ANONYMOUS_P (nt))
-		error ("%qT is/uses anonymous type", t);
-	      else
-		error ("template argument for %qD uses local type %qT",
-		       tmpl, t);
-	    }
-	  return true;
-	}
-      /* In order to avoid all sorts of complications, we do not
-	 allow variably-modified types as template arguments.  */
-      else if (variably_modified_type_p (t, NULL_TREE))
+      if (variably_modified_type_p (t, NULL_TREE))
 	{
 	  if (complain & tf_error)
 	    error ("%qT is a variably modified type", t);
diff --git a/gcc/testsuite/g++.dg/ext/anon-struct4.C b/gcc/testsuite/g++.dg/ext/anon-struct4.C
index 4f0fcd1..53302d8 100644
--- a/gcc/testsuite/g++.dg/ext/anon-struct4.C
+++ b/gcc/testsuite/g++.dg/ext/anon-struct4.C
@@ -1,4 +1,3 @@
 // PR c++/14401
 
 struct { struct { int& i ; } bar ; } foo ; // { dg-error "uninitialized" "uninit" }
-// { dg-warning "anonymous" "anon" { target *-*-* } 3 }
diff --git a/gcc/testsuite/g++.dg/lookup/anon2.C b/gcc/testsuite/g++.dg/lookup/anon2.C
index d556ba0..3143b62 100644
--- a/gcc/testsuite/g++.dg/lookup/anon2.C
+++ b/gcc/testsuite/g++.dg/lookup/anon2.C
@@ -1,9 +1,9 @@
 // { dg-do compile }
 // { dg-options "" }
 
-// Make sure we issue a diagnostic if a type with no linkage is used
-// to declare a a variable that has linkage.
+// Make sure we don't issue a diagnostic if a type with no linkage is used
+// to declare a a variable that has linkage if that variable is defined.
 
-struct { int i; } a; // { dg-warning "anonymous type" }
+struct { int i; } a;
 
 void foo() { a.i; }
diff --git a/gcc/testsuite/g++.dg/other/anon3.C b/gcc/testsuite/g++.dg/other/anon3.C
index 87116eb..87cbfb5 100644
--- a/gcc/testsuite/g++.dg/other/anon3.C
+++ b/gcc/testsuite/g++.dg/other/anon3.C
@@ -4,4 +4,4 @@
 
 // { dg-do compile }
 
-enum { a = 3 } x; // { dg-warning "anonymous type" }
+enum { a = 3 } x;
diff --git a/gcc/testsuite/g++.dg/other/linkage2.C b/gcc/testsuite/g++.dg/other/linkage2.C
new file mode 100644
index 0000000..4e3e6f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/linkage2.C
@@ -0,0 +1,31 @@
+// DR 743: A type without linkage shall not be used as the type of a
+//     variable or function with linkage, unless
+//   o the variable or function has extern "C" linkage (7.5 [dcl.link]), or
+//   o the variable or function is not used (3.2 [basic.def.odr]) or is
+//   defined in the same translation unit.
+
+template <typename T> struct B {
+  void g(T){}
+  void h(T);			// { dg-error "never defined" }
+  friend void i(B, T){}
+  static T t1;			// { dg-error "never defined" }
+  static T t2;
+};
+
+template <typename T> T B<T>::t2 = { };
+
+enum {} e1;			// OK, defined
+extern enum {} e2;		// { dg-error "never defined" }
+extern "C" enum {} e3;		// OK, extern "C"
+
+void f() {
+  struct A { int x; };  // no linkage
+  A a = {1};
+  B<A> ba;              // declares B<A>::g(A) and B<A>::h(A)
+  ba.t1 = a;		// error, B<T>::t never defined
+  ba.t2 = a;		// OK
+  ba.g(a);              // OK
+  ba.h(a);              // error, B<T>::h never defined
+  i(ba, a);             // OK
+  e1+e2+e3;
+}
diff --git a/gcc/testsuite/g++.dg/template/arg2.C b/gcc/testsuite/g++.dg/template/arg2.C
index 9fb7a68..1314b25 100644
--- a/gcc/testsuite/g++.dg/template/arg2.C
+++ b/gcc/testsuite/g++.dg/template/arg2.C
@@ -10,5 +10,5 @@ template <typename T> class X {};
 void fn ()
 {
   class L {};
-  X<L> f; // { dg-error "uses local type|trying to instantiate|no type|invalid type" "" }
+  X<L> f;
 }
diff --git a/gcc/testsuite/g++.dg/template/local4.C b/gcc/testsuite/g++.dg/template/local4.C
index cfa3736..41e2370 100644
--- a/gcc/testsuite/g++.dg/template/local4.C
+++ b/gcc/testsuite/g++.dg/template/local4.C
@@ -4,5 +4,5 @@ template <typename T> void foo() {}
 
 int main () {
   struct S {};
-  foo<S> (); // { dg-error "match" } 
+  foo<S> ();
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.law/operators32.C b/gcc/testsuite/g++.old-deja/g++.law/operators32.C
index 91de03e..89f0b66 100644
--- a/gcc/testsuite/g++.old-deja/g++.law/operators32.C
+++ b/gcc/testsuite/g++.old-deja/g++.law/operators32.C
@@ -49,7 +49,7 @@ foo() {std::cout << "foo created" << std::endl; }
 };
 
 foo **f2;
-allocate2d(d1, d2, f2);// { dg-error "" }  type.*// ERROR -    trying to.*
-ffree(d1, f2);// { dg-error "" }  type.*// ERROR -    trying to.*
+allocate2d(d1, d2, f2);
+ffree(d1, f2);
 
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.other/anon9.C b/gcc/testsuite/g++.old-deja/g++.other/anon9.C
index a364db8..f4b1923 100644
--- a/gcc/testsuite/g++.old-deja/g++.other/anon9.C
+++ b/gcc/testsuite/g++.old-deja/g++.other/anon9.C
@@ -4,3 +4,8 @@
 
 typedef const struct { int i; } T; // { dg-error "" } referenced below
 void f (T* t);			// { dg-error "" } uses unnamed type
+
+int main()
+{
+  f(0);
+}
diff --git a/gcc/testsuite/g++.old-deja/g++.other/linkage1.C b/gcc/testsuite/g++.old-deja/g++.other/linkage1.C
index e9b5a9d..de9a6ac 100644
--- a/gcc/testsuite/g++.old-deja/g++.other/linkage1.C
+++ b/gcc/testsuite/g++.old-deja/g++.other/linkage1.C
@@ -3,13 +3,16 @@ typedef struct {
   int i;
 } *p;
 
-void f (p) { }			// { dg-error "uses anonymous type" }
-p q;				// { dg-warning "uses anonymous type" } 
+void f (p) { }
+p q;
 
 int main()
 {
-  extern p j;			// { dg-warning "uses anonymous type" }
+  extern p j;			// { dg-error "anonymous type" }
+  j+1;
   struct A { int j; };
-  extern A a;			// { dg-warning "uses local type" }
-  extern void f (A);		// { dg-error "uses local type" }
+  extern A a;			// { dg-error "local type" }
+  a.j+1;
+  extern void f (A);		// { dg-error "local type" }
+  f(a);
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.other/linkage2.C b/gcc/testsuite/g++.old-deja/g++.other/linkage2.C
index 2385b22..64f74f7 100644
--- a/gcc/testsuite/g++.old-deja/g++.other/linkage2.C
+++ b/gcc/testsuite/g++.old-deja/g++.other/linkage2.C
@@ -7,7 +7,7 @@ extern GDBM_FILE gdbm_open();
 }
 
 typedef struct { int dummy[10]; } *FAIL_FILE;
-extern FAIL_FILE fail_open(); // { dg-error "" } non-local function
+extern FAIL_FILE fail_open(); // OK because it's never used
 
 typedef struct { int dummy[10]; } *SUCCESS_FILE, S;
 extern SUCCESS_FILE success_open();
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/enum6.C b/gcc/testsuite/g++.old-deja/g++.pt/enum6.C
index 254b48b..561254d 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/enum6.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/enum6.C
@@ -8,7 +8,7 @@ void fn(T)
 {
   enum tern { H, L, X, U };
 
-  vector<tern> ternvec; // { dg-error "" } composed from a local type
+  vector<tern> ternvec;
 }
 
 template void fn(int);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]