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 allow more deduction of std::initializer_list


The compromise initializer-list proposal allowed deduction of std::initializer_list for auto as in 'auto il = { 24 };', but not for template parameters, as some were concerned that it would cause overload resolution problems. This patch implements deduction for template parameters so that people can test it out to see if any problems come up, and gives a pedwarn so people are aware when it happens.

Tested x86_64-pc-linux-gnu, applying to trunk and 4.4.

Jason
commit b3399cda03702c621e320582b823fed2284a1eb8
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Sep 14 15:03:24 2009 -0400

    	* decl2.c (determine_visibility): Make anonymous types internal.
    	(mark_used): Complain about types without linkage used in
    	decls with internal linkage.
    	(vague_linkage_fn_p): Split out from...
    	* decl.c (maybe_commonize_var): ...here.
    	(grokdeclarator): Adjust linkage when a typedef gives linkage name.
    	* tree.c (no_linkage_check): Check the enclosing class and template
    	arguments.
    
    	* cp-tree.h (TYPE_NAMESPACE_SCOPE_P): New.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7801f98..816b4ca 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2129,6 +2129,9 @@ struct GTY(()) lang_decl {
   (!DECL_TEMPLATE_PARM_P (NODE)					\
    && TREE_CODE (CP_DECL_CONTEXT (NODE)) == NAMESPACE_DECL)
 
+#define TYPE_NAMESPACE_SCOPE_P(NODE) \
+  (TREE_CODE (CP_TYPE_CONTEXT (NODE)) == NAMESPACE_DECL)
+
 /* 1 iff NODE is a class member.  */
 #define DECL_CLASS_SCOPE_P(NODE) \
   (DECL_CONTEXT (NODE) && TYPE_P (DECL_CONTEXT (NODE)))
@@ -4457,6 +4460,7 @@ extern bool check_java_method			(tree);
 extern tree build_memfn_type			(tree, tree, cp_cv_quals);
 extern void maybe_retrofit_in_chrg		(tree);
 extern void maybe_make_one_only			(tree);
+extern bool vague_linkage_fn_p			(tree);
 extern void grokclassfn				(tree, tree,
 						 enum overload_flags);
 extern tree grok_array_decl			(tree, tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 0cfcb4f..614dbb0 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -4615,13 +4615,7 @@ maybe_commonize_var (tree decl)
       /* Don't mess with __FUNCTION__.  */
       && ! DECL_ARTIFICIAL (decl)
       && DECL_FUNCTION_SCOPE_P (decl)
-      /* Unfortunately, import_export_decl has not always been called
-	 before the function is processed, so we cannot simply check
-	 DECL_COMDAT.  */
-      && (DECL_COMDAT (DECL_CONTEXT (decl))
-	  || ((DECL_DECLARED_INLINE_P (DECL_CONTEXT (decl))
-	       || DECL_TEMPLATE_INSTANTIATION (DECL_CONTEXT (decl)))
-	      && TREE_PUBLIC (DECL_CONTEXT (decl)))))
+      && vague_linkage_fn_p (DECL_CONTEXT (decl)))
     {
       if (flag_weak)
 	{
@@ -8845,6 +8839,10 @@ grokdeclarator (const cp_declarator *declarator,
 	    DECL_NAME (CLASSTYPE_TI_TEMPLATE (type))
 	      = TYPE_IDENTIFIER (type);
 
+	  /* Adjust linkage now that we aren't anonymous anymore.  */
+	  set_linkage_according_to_type (type, TYPE_MAIN_DECL (type));
+	  determine_visibility (TYPE_MAIN_DECL (type));
+
 	  /* FIXME remangle member functions; member functions of a
 	     type with external linkage have external linkage.  */
 	}
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index eb48c55..3e8c0d7 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1605,6 +1605,22 @@ maybe_make_one_only (tree decl)
     }
 }
 
+/* Returns true iff DECL, a FUNCTION_DECL, has vague linkage.  This
+   predicate will give the right answer during parsing of the function,
+   which other tests may not.  */
+
+bool
+vague_linkage_fn_p (tree fn)
+{
+  /* Unfortunately, import_export_decl has not always been called
+     before the function is processed, so we cannot simply check
+     DECL_COMDAT.  */
+  return (DECL_COMDAT (fn)
+	  || ((DECL_DECLARED_INLINE_P (fn)
+	       || DECL_TEMPLATE_INSTANTIATION (fn))
+	      && TREE_PUBLIC (fn)));
+}
+
 /* Determine whether or not we want to specifically import or export CTYPE,
    using various heuristics.  */
 
@@ -2075,6 +2091,16 @@ determine_visibility (tree decl)
 	  || ! DECL_VISIBILITY_SPECIFIED (decl))
 	constrain_visibility (decl, tvis);
     }
+  else if (no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/true))
+    /* 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.
+
+       Since non-extern "C" decls need to be defined in the same
+       translation unit, we can make the type internal.  */
+    constrain_visibility (decl, VISIBILITY_ANON);
 
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
@@ -3966,7 +3992,7 @@ mark_used (tree decl)
    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)
+  if (decl_linkage (decl) != lk_none
       && !DECL_EXTERN_C_P (decl)
       && !DECL_ARTIFICIAL (decl)
       && !decl_defined_p (decl)
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 808a18a..51752a3 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1513,7 +1513,7 @@ verify_stmt_tree (tree t)
 
 /* Check if the type T depends on a type with no linkage and if so, return
    it.  If RELAXED_P then do not consider a class type declared within
-   a TREE_PUBLIC function to have no linkage.  */
+   a vague-linkage function to have no linkage.  */
 
 tree
 no_linkage_check (tree t, bool relaxed_p)
@@ -1527,8 +1527,6 @@ no_linkage_check (tree t, bool relaxed_p)
 
   switch (TREE_CODE (t))
     {
-      tree fn;
-
     case RECORD_TYPE:
       if (TYPE_PTRMEMFUNC_P (t))
 	goto ptrmem;
@@ -1536,13 +1534,42 @@ no_linkage_check (tree t, bool relaxed_p)
     case UNION_TYPE:
       if (!CLASS_TYPE_P (t))
 	return NULL_TREE;
+
+      /* Check template type-arguments.  I think that types with no linkage
+         can't occur in non-type arguments, though that might change with
+         constexpr.  */
+      r = CLASSTYPE_TEMPLATE_INFO (t);
+      if (r)
+	{
+	  tree args = INNERMOST_TEMPLATE_ARGS (TI_ARGS (r));
+	  int i;
+
+	  for (i = TREE_VEC_LENGTH (args); i-- > 0; )
+	    {
+	      tree elt = TREE_VEC_ELT (args, i);
+	      if (TYPE_P (elt)
+		  && (r = no_linkage_check (elt, relaxed_p), r))
+		return r;
+	    }
+	}
       /* Fall through.  */
     case ENUMERAL_TYPE:
-      if (TYPE_ANONYMOUS_P (t))
-	return t;
-      fn = decl_function_context (TYPE_MAIN_DECL (t));
-      if (fn && (!relaxed_p || !TREE_PUBLIC (fn)))
+      /* Only treat anonymous types as having no linkage if they're at
+	 namespace scope.  This doesn't have a core issue number yet.  */
+      if (TYPE_ANONYMOUS_P (t) && TYPE_NAMESPACE_SCOPE_P (t))
 	return t;
+
+      r = CP_TYPE_CONTEXT (t);
+      if (TYPE_P (r))
+	return no_linkage_check (TYPE_CONTEXT (t), relaxed_p);
+      else if (TREE_CODE (r) == FUNCTION_DECL)
+	{
+	  if (!relaxed_p || !TREE_PUBLIC (r) || !vague_linkage_fn_p (r))
+	    return t;
+	  else
+	    return no_linkage_check (CP_DECL_CONTEXT (r), relaxed_p);
+	}
+
       return NULL_TREE;
 
     case ARRAY_TYPE:
diff --git a/gcc/testsuite/g++.dg/cpp0x/nolinkage1.C b/gcc/testsuite/g++.dg/cpp0x/nolinkage1.C
new file mode 100644
index 0000000..a31394b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nolinkage1.C
@@ -0,0 +1,21 @@
+// DR 757 allows using types without linkage in declarations with linkage.
+// Test that this doesn't lead to link-time collisions.
+
+// { dg-additional-sources "nolinkage1a.cc" }
+// { dg-do link }
+
+#include "nolinkage1.h"
+
+typedef struct { int i; } *AP;
+
+void f(AP) { }
+
+A<AP> a;
+
+static void g()
+{
+  struct B { };
+  A<B> a;
+}
+
+int main() { g(); f(0); }
diff --git a/gcc/testsuite/g++.dg/cpp0x/nolinkage1.h b/gcc/testsuite/g++.dg/cpp0x/nolinkage1.h
new file mode 100644
index 0000000..3cb5f63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nolinkage1.h
@@ -0,0 +1,8 @@
+template <class T>
+struct A
+{
+  A();
+};
+
+template <class T>
+A<T>::A() {  }
diff --git a/gcc/testsuite/g++.dg/cpp0x/nolinkage1a.cc b/gcc/testsuite/g++.dg/cpp0x/nolinkage1a.cc
new file mode 100644
index 0000000..f8528f3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nolinkage1a.cc
@@ -0,0 +1,15 @@
+#include "nolinkage1.h"
+
+typedef struct { double d; } *BP;
+
+void f(BP) {  }
+
+A<BP> b;
+
+static void g()
+{
+  struct B { };
+  A<B> a;
+}
+
+int dummy() { g(); f(0); }

commit 99ddd5611e5e43a200e23415dfa2bb93c20cda42
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Sep 10 17:06:32 2009 -0400

    	* c.opt: Add -fno-deduce-init-list.
    	* pt.c (get_pattern_parm): New.
    	(listify): Split out from...
    	(listify_autos): ...here.
    	(unify): Deduce std::initializer_list for T.
    	* call.c (build_over_call): Warn about it.

diff --git a/gcc/c.opt b/gcc/c.opt
index 5ee9a13..21f7ab7 100644
--- a/gcc/c.opt
+++ b/gcc/c.opt
@@ -558,6 +558,10 @@ fconstant-string-class=
 ObjC ObjC++ Joined
 -fconst-string-class=<name>	Use class <name> for constant strings
 
+fdeduce-init-list
+C++ ObjC++ Var(flag_deduce_init_list) Init(1)
+-fno-deduce-init-list	disable deduction of std::initializer_list for a template type parameter from a brace-enclosed initializer-list
+
 fdefault-inline
 C++ ObjC++
 Inline member functions by default
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f4b0497..8c1bb0e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5568,6 +5568,28 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	  && !TREE_ADDRESSABLE (type))
 	conv = conv->u.next;
 
+      /* Warn about initializer_list deduction that isn't currently in the
+	 working draft.  */
+      if (cxx_dialect > cxx98
+	  && flag_deduce_init_list
+	  && cand->template_decl
+	  && is_std_init_list (non_reference (type)))
+	{
+	  tree tmpl = TI_TEMPLATE (cand->template_decl);
+	  tree realparm = chain_index (j, DECL_ARGUMENTS (cand->fn));
+	  tree patparm = get_pattern_parm (realparm, tmpl);
+
+	  if (!is_std_init_list (non_reference (TREE_TYPE (patparm))))
+	    {
+	      pedwarn (input_location, 0, "deducing %qT as %qT",
+		       non_reference (TREE_TYPE (patparm)),
+		       non_reference (type));
+	      pedwarn (input_location, 0, "  in call to %q+D", cand->fn);
+	      pedwarn (input_location, 0,
+		       "  (you can disable this with -fno-deduce-init-list)");
+	    }
+	}
+
       val = convert_like_with_context
 	(conv, VEC_index (tree, args, arg_index), fn, i - is_method,
 	 complain);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e8db635..7801f98 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4646,6 +4646,7 @@ extern tree get_types_needing_access_check	(tree);
 extern int template_class_depth			(tree);
 extern int is_specialization_of			(tree, tree);
 extern bool is_specialization_of_friend		(tree, tree);
+extern tree get_pattern_parm			(tree, tree);
 extern int comp_template_args			(tree, tree);
 extern tree maybe_process_partial_specialization (tree);
 extern tree most_specialized_instantiation	(tree);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 9f094a3..7e3a545 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -192,6 +192,8 @@ static void perform_typedefs_access_check (tree tmpl, tree targs);
 static void append_type_to_template_for_access_check_1 (tree, tree, tree);
 static hashval_t iterative_hash_template_arg (tree arg, hashval_t val);
 static bool primary_template_instantiation_p (const_tree);
+static tree listify (tree);
+static tree listify_autos (tree, tree);
 
 /* Make the current scope suitable for access checking when we are
    processing T.  T can be FUNCTION_DECL for instantiated function
@@ -8033,6 +8035,36 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
   return result;
 }
 
+/* Given PARM_DECL PARM, find the corresponding PARM_DECL in the template
+   TMPL.  We do this using DECL_PARM_INDEX, which should work even with
+   parameter packs; all parms generated from a function parameter pack will
+   have the same DECL_PARM_INDEX.  */
+
+tree
+get_pattern_parm (tree parm, tree tmpl)
+{
+  tree pattern = DECL_TEMPLATE_RESULT (tmpl);
+  tree patparm;
+
+  if (DECL_ARTIFICIAL (parm))
+    {
+      for (patparm = DECL_ARGUMENTS (pattern);
+	   patparm; patparm = TREE_CHAIN (patparm))
+	if (DECL_ARTIFICIAL (patparm)
+	    && DECL_NAME (parm) == DECL_NAME (patparm))
+	  break;
+    }
+  else
+    {
+      patparm = FUNCTION_FIRST_USER_PARM (DECL_TEMPLATE_RESULT (tmpl));
+      patparm = chain_index (DECL_PARM_INDEX (parm)-1, patparm);
+      gcc_assert (DECL_PARM_INDEX (patparm)
+		  == DECL_PARM_INDEX (parm));
+    }
+
+  return patparm;
+}
+
 /* Substitute ARGS into the vector or list of template arguments T.  */
 
 static tree
@@ -13727,6 +13759,12 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict)
     {
       tree elt, elttype;
       unsigned i;
+      tree orig_parm = parm;
+
+      /* Replace T with std::initializer_list<T> for deduction.  */
+      if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
+	  && flag_deduce_init_list)
+	parm = listify (parm);
 
       if (!is_std_init_list (parm))
 	/* We can only deduce from an initializer list argument if the
@@ -13752,6 +13790,16 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict)
 	  if (unify (tparms, targs, elttype, elt, elt_strict))
 	    return 1;
 	}
+
+      /* If the std::initializer_list<T> deduction worked, replace the
+	 deduced A with std::initializer_list<A>.  */
+      if (orig_parm != parm)
+	{
+	  idx = TEMPLATE_TYPE_IDX (orig_parm);
+	  targ = TREE_VEC_ELT (INNERMOST_TEMPLATE_ARGS (targs), idx);
+	  targ = listify (targ);
+	  TREE_VEC_ELT (INNERMOST_TEMPLATE_ARGS (targs), idx) = targ;
+	}
       return 0;
     }
 
@@ -17446,26 +17494,33 @@ make_auto (void)
   return au;
 }
 
-/* Replace auto in TYPE with std::initializer_list<auto>.  */
+/* Given type ARG, return std::initializer_list<ARG>.  */
 
 static tree
-listify_autos (tree type, tree auto_node)
+listify (tree arg)
 {
   tree std_init_list = namespace_binding
     (get_identifier ("initializer_list"), std_node);
   tree argvec;
-  tree init_auto;
   if (!std_init_list || !DECL_CLASS_TEMPLATE_P (std_init_list))
     {    
-      error ("deducing auto from brace-enclosed initializer list requires "
+      error ("deducing from brace-enclosed initializer list requires "
 	     "#include <initializer_list>");
       return error_mark_node;
     }
   argvec = make_tree_vec (1);
-  TREE_VEC_ELT (argvec, 0) = auto_node;
-  init_auto = lookup_template_class (std_init_list, argvec, NULL_TREE,
-				     NULL_TREE, 0, tf_warning_or_error);
+  TREE_VEC_ELT (argvec, 0) = arg;
+  return lookup_template_class (std_init_list, argvec, NULL_TREE,
+				NULL_TREE, 0, tf_warning_or_error);
+}
+
+/* Replace auto in TYPE with std::initializer_list<auto>.  */
 
+static tree
+listify_autos (tree type, tree auto_node)
+{
+  tree init_auto = listify (auto_node);
+  tree argvec = make_tree_vec (1);
   TREE_VEC_ELT (argvec, 0) = init_auto;
   if (processing_template_decl)
     argvec = add_to_template_args (current_template_args (), argvec);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5a96820..ad8e51c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1785,6 +1785,27 @@ two definitions were merged.
 This option is no longer useful on most targets, now that support has
 been added for putting variables into BSS without making them common.
 
+@item -fno-deduce-init-list
+@opindex fno-deduce-init-list
+Disable deduction of a template type parameter as
+std::initializer_list from a brace-enclosed initializer list, i.e.
+
+@smallexample
+template <class T> auto forward(T t) -> decltype (realfn (t))
+@{
+  return realfn (t);
+@}
+
+void f()
+@{
+  forward(@{1,2@}); // call forward<std::initializer_list<int>>
+@}
+@end smallexample
+
+This option is present because this deduction is an extension to the
+current specification in the C++0x working draft, and there was
+some concern about potential overload resolution problems.
+
 @item -ffriend-injection
 @opindex ffriend-injection
 Inject friend functions into the enclosing namespace, so that they are
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-deduce.C b/gcc/testsuite/g++.dg/cpp0x/initlist-deduce.C
new file mode 100644
index 0000000..e422132
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-deduce.C
@@ -0,0 +1,26 @@
+// Test for deduction of T as std::initializer_list.  This isn't currently
+// supported by the working draft, but is necessary for perfect forwarding
+// of initializer-lists to things that can take a std::initializer_list.
+
+// { dg-options -std=c++0x }
+// { dg-do run }
+
+#include <initializer_list>
+
+struct A
+{
+  A(std::initializer_list<int>) { }
+};
+
+void f (A a) { }
+
+template <class T>
+auto g (T&& t) -> decltype (f(t)) // { dg-warning "call" }
+{
+  return f(t);
+}
+
+int main()
+{
+  g({1});			// { dg-warning "deduc" }
+}

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