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. gcc/cp/ChangeLog: PR c++/68948 * pt.c (tsubst_baselink): Diagnose an invalid constructor call if lookup_fnfields returns NULL_TREE and name we're looking up has the form A::A. gcc/testsuite/ChangeLog: PR c++/68948 * g++.dg/template/pr68948.C: New test. --- gcc/cp/pt.c | 10 +++++++- gcc/testsuite/g++.dg/template/pr68948.C | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/template/pr68948.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 4d405cf..91d0ef2 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -13583,7 +13583,15 @@ tsubst_baselink (tree baselink, tree object_type, name = mangle_conv_op_name_for_type (optype); baselink = lookup_fnfields (qualifying_scope, name, /*protect=*/1); if (!baselink) - return error_mark_node; + { + if (constructor_name_p (name, qualifying_scope)) + { + if (complain & tf_error) + error ("cannot call constructor %<%T::%D%> directly", + qualifying_scope, name); + } + return error_mark_node; + } /* If lookup found a single function, mark it as used at this point. (If it lookup found multiple functions the one selected diff --git a/gcc/testsuite/g++.dg/template/pr68948.C b/gcc/testsuite/g++.dg/template/pr68948.C new file mode 100644 index 0000000..ccbfb19 --- /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 (); // { dg-error "cannot call constructor" } + B::B (0); // { dg-error "cannot call constructor" } + } +}; + +/* 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 (); // { dg-error "cannot call constructor" } + B::B (0); // { dg-error "cannot call constructor" } + Time::B (0); + } +}; + +int +main (void) +{ + A<int> a; + C<int> c; + a.TestBody (); + c.TestBody (); +} -- 2.7.0.303.g36d4cae