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]

Re: [PATCH] Fix PR c++/68948 (wrong code generation due to invalid constructor call)


On Thu, 4 Feb 2016, Patrick Palka wrote:

The compiler correctly detects and diagnoses invalid constructor calls
such as C::C () in a non-template context but it fails to do so while
processing a class template.  [ Section 3.4.3.1 of the standard is what
makes these forms of constructor calls illegal -- see
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68948#c9  ]

In a non-template context this diagnosis would take place in
build_new_method_call, called from finish_call_expr, but while
processing a class template we may exit early out of finish_call_expr
and never call build_new_method_call.

Thus we never diagnose this invalid constructor call during template
processing.  So then during instantiation of the enclosing template we
call tsubst_baselink on this constructor call, during which the call to
lookup_fnfields returns NULL (because it finds the injected class type C
not the constructor C).  Because the lookup failed, tsubst_baselink
returns error_mark_node and this node persists all the way through to
gimplification where it silently gets discarded.

This patch fixes this problem by diagnosing these invalid constructor
calls in tsubst_baselink.  Alternatively, we can rewire finish_call_expr
avoid exiting early while processing a template if the call in question
is a constructor call.  I'm not sure which approach is better.  This
approach seems more conservative, since it's just attaching an error
message to an existing error path.

And here is the other approach, which rewires finish_call_expr:

-- >8 --

gcc/cp/ChangeLog:

	PR c++/68948
	* semantics.c (finish_call_expr): Don't assume a constructor
	call is dependent if the "this" pointer is dependent.

gcc/testsuite/ChangeLog:

	PR c++/68948
	* g++.dg/template/pr68948.C: New test.
---
 gcc/cp/semantics.c                      | 14 +++++++++--
 gcc/testsuite/g++.dg/template/pr68948.C | 41 +++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/pr68948.C

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 95c4f19..31c03ae 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2270,6 +2270,7 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 	     related to CWG issues 515 and 1005.  */
 	  || (TREE_CODE (fn) != COMPONENT_REF
 	      && non_static_member_function_p (fn)
+	      && !DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (get_first_fn (fn))
 	      && current_class_ref
 	      && type_dependent_expression_p (current_class_ref)))
 	{
@@ -2348,8 +2349,17 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 	[class.access.base] says that we need to convert 'this' to B* as
 	part of the access, so we pass 'B' to maybe_dummy_object.  */

-      object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)),
-				   NULL);
+      if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (get_first_fn (fn)))
+	{
+	  /* A constructor call always uses a dummy object.  (This constructor
+	     call which has the form A::A () is actually invalid and we are
+	     going to reject it later in build_new_method_call.)  */
+	  object = build_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)));
+	  gcc_assert (!type_dependent_expression_p (object));
+	}
+      else
+	object = maybe_dummy_object (BINFO_TYPE (BASELINK_ACCESS_BINFO (fn)),
+				     NULL);

       if (processing_template_decl)
 	{
diff --git a/gcc/testsuite/g++.dg/template/pr68948.C b/gcc/testsuite/g++.dg/template/pr68948.C
new file mode 100644
index 0000000..3cb6844
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/pr68948.C
@@ -0,0 +1,41 @@
+// PR c++/68948
+
+struct B { B (); B (int); };
+
+struct Time : B { };
+
+/* Here, A and B are unrelated types.  */
+
+template <typename>
+struct A
+{
+  void TestBody ()
+  {
+    B::B (); // { dg-error "cannot call constructor .B::B." }
+    B::B::B (); // { dg-error "cannot call constructor .B::B." }
+    B::B (0); // { dg-error "cannot call constructor .B::B." }
+  }
+};
+
+/* Here, C is (indirectly) derived from B.  */
+
+template <typename g>
+struct C : Time
+{
+  void TestBody ()
+  {
+    B::B (); // { dg-error "cannot call constructor .B::B." }
+    B::B::B (); // { dg-error "cannot call constructor .B::B." }
+    B::B (0); // { dg-error "cannot call constructor .B::B." }
+    Time::B (0);
+  }
+};
+
+int
+main (void)
+{
+  A<int> a;
+  C<int> c;
+  a.TestBody ();
+  c.TestBody ();
+}
--
2.7.0.303.g36d4cae


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