PR 66443 concerns C++14 DR1611. It is now permitted to use the base-ctor of an
abstract class whos complete ctor is deleted because of a virtual base issue.
Specifically, given
class A {
A (int);
// no default ctor in C++14
};
class B : virtual A {
virtual void Foo () = 0; // abstract
// B::B deleted because there's no A::A()
};
class C: B
{
virtual void Foo (); // not abstract
C ()
: A(1) // explicit vbase construction
{}
};
Here, there's no way that B's complete object constructor can be called -- it's
an abstract class, so no complete B objects can exist. B's as-a-base
constructor never constructs the A base, because its a virtual base. So the
missing 'A::A()' is never needed. C's constructors must explicitly construct
the A base using the available constructors.
the trickiness is that we create both base and complete constructors by building
a more generic constructor, cloning it substituting the 'in-charge' parameter
value and then allowing optimization to remove unreachable code in the two
instances. (see below for why not have the complete-ctor tail call the base-ctor.)
This patch adds a new FUNCTION_DECL flag and accessor 'DECL_BASE_FN_UNDELETED'.
In the example above, this is true for B's generic constructor. When we
actually need a B constructor (the bodies are created lazily), we check
DECL_BASE_FN_UNDELETED to see whether we need to do something 'interesting'. If
we do, and its the base-ctor we don't do the usual 'create generic body, clone
it' scheme. Instead we create the base body explicitly, passing an new
'skip-vbases' flag, and thus never try and find 'A::A()' in this case.
build_if_incharge needs tweaking, because the incharge parm only exists on the
generic ctor.
bootstrapped on x86_64-linux, ok?
nathan
[*] why not tail-call the as-base ctor after constructing the vbases? That
would be fine for most cases, but we might need to provide a vtt pointer
describing the complete object. Not sure if such a table exists right now.
Anyway the more serios issue is varadic ctors. [without additional compiler
magic] we can't tailcall from the complete ctor to the base ctor.
2016-09-30 Nathan Sidwell <nat...@acm.org>
cp/
PR c++/66443
* cp-tree.h (lang_decl_fn): Add base_undeleted_p.
(DECL_BASE_UNDELETED_FN): New.
(emit_mem_initializer, finish_mem_initializers): Add skip vbases arg.
* class.c (build_if_incharge): Allow no incharge parm when
building undeleted base dtor directly.
* decl2.c (mark_used): Don't complain about using an undeleted
base ctor.
* init.c (emit_mem_initializers): Add skip_vbases arg. Skip vbase
construction if true.
* method.c (do_build_copy_constructor): Add skip_vbases arg, pass
it through.
(synthesize_method): Skip vbases when we have a
(synthesized_method_walk): Add vbase_deleted_p argument. Use it.
(get_defaulted_eh_spec): Adjust synthesized_method_walk.
(maybe_explain_implicit_delete, explain_implicit_non_constexpr,
deduce_intheriting_ctor): Likewise.
(implicitly_declare_fn): Determine DECL_BASE_UNDELETED_FN.
* parser.c (cp_parser_ctor_initializer,
cp_parser_mem_initializer_list): Adjust finish_mem_initializer call.
* pt.c (tsubst_expr): Likewise.
* semantics.c (finish_mem_initializers): Add skip_vbase parm, pass
to emit_mem_initializers.
testsuite.
PR c++/66443
* g++.dg/cpp0x/pr66443-cxx11.C: New.
* g++.dg/cpp1y/pr66443-cxx14.C: New
* g++.dg/cpp1y/pr66443-cxx14-2.C: New.
Index: cp/class.c
===================================================================
--- cp/class.c (revision 240596)
+++ cp/class.c (working copy)
@@ -233,15 +233,27 @@ int n_inner_fields_searched = 0;
tree
build_if_in_charge (tree true_stmt, tree false_stmt)
{
- gcc_assert (DECL_HAS_IN_CHARGE_PARM_P (current_function_decl));
- tree cmp = build2 (NE_EXPR, boolean_type_node,
- current_in_charge_parm, integer_zero_node);
- tree type = unlowered_expr_type (true_stmt);
- if (VOID_TYPE_P (type))
- type = unlowered_expr_type (false_stmt);
- tree cond = build3 (COND_EXPR, type,
- cmp, true_stmt, false_stmt);
- return cond;
+ tree result;
+
+ if (DECL_HAS_IN_CHARGE_PARM_P (current_function_decl))
+ {
+ tree cmp = build2 (NE_EXPR, boolean_type_node,
+ current_in_charge_parm, integer_zero_node);
+ tree type = unlowered_expr_type (true_stmt);
+ if (VOID_TYPE_P (type))
+ type = unlowered_expr_type (false_stmt);
+ result = build3 (COND_EXPR, type, cmp, true_stmt, false_stmt);
+ }
+ else
+ {
+ /* We must be building an undeleted base ctor. */
+ gcc_assert (DECL_BASE_CONSTRUCTOR_P (current_function_decl)
+ && DECL_DELETED_FN (current_function_decl)
+ && DECL_BASE_UNDELETED_FN (current_function_decl));
+ result = false_stmt;
+ }
+
+ return result;
}
/* Convert to or from a base subobject. EXPR is an expression of type
Index: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h (revision 240596)
+++ cp/cp-tree.h (working copy)
@@ -2297,6 +2297,7 @@ struct GTY(()) lang_decl_fn {
unsigned static_function : 1;
unsigned pure_virtual : 1;
unsigned defaulted_p : 1;
+ unsigned base_undeleted_p : 1;
unsigned has_in_charge_parm_p : 1;
unsigned has_vtt_parm_p : 1;
@@ -2306,7 +2307,7 @@ struct GTY(()) lang_decl_fn {
unsigned this_thunk_p : 1;
unsigned hidden_friend_p : 1;
unsigned omp_declare_reduction_p : 1;
- /* 2 spare bits on 32-bit hosts, 34 on 64-bit hosts. */
+ /* 1 spare bit on 32-bit hosts, 33 on 64-bit hosts. */
/* For a non-thunk function decl, this is a tree list of
friendly classes. For a thunk function decl, it is the
@@ -3631,6 +3632,10 @@ more_aggr_init_expr_args_p (const aggr_i
#define DECL_DELETED_FN(DECL) \
(LANG_DECL_FN_CHECK (DECL)->min.base.threadprivate_or_deleted_p)
+/* Nonzero if the base-specific function is not deleted. */
+#define DECL_BASE_UNDELETED_FN(DECL) \
+ (LANG_DECL_FN_CHECK (DECL)->base_undeleted_p)
+
/* Nonzero if DECL was declared with '= default' (maybe implicitly). */
#define DECL_DEFAULTED_FN(DECL) \
(LANG_DECL_FN_CHECK (DECL)->defaulted_p)
@@ -5960,7 +5965,7 @@ extern tree do_friend (tree, tree, tr
/* in init.c */
extern tree expand_member_init (tree);
-extern void emit_mem_initializers (tree);
+extern void emit_mem_initializers (tree, bool);
extern tree build_aggr_init (tree, tree, int,
tsubst_flags_t);
extern int is_class_type (tree, int);
@@ -6413,7 +6418,7 @@ extern tree finish_offsetof (tree, loc
extern void finish_decl_cleanup (tree, tree);
extern void finish_eh_cleanup (tree);
extern void emit_associated_thunks (tree);
-extern void finish_mem_initializers (tree);
+extern void finish_mem_initializers (tree, bool);
extern tree check_template_template_default_arg (tree);
extern bool expand_or_defer_fn_1 (tree);
extern void expand_or_defer_fn (tree);
Index: cp/decl2.c
===================================================================
--- cp/decl2.c (revision 240596)
+++ cp/decl2.c (working copy)
@@ -5132,38 +5132,37 @@ mark_used (tree decl, tsubst_flags_t com
if (TREE_CODE (decl) == TEMPLATE_DECL)
return true;
- if (DECL_CLONED_FUNCTION_P (decl))
- TREE_USED (DECL_CLONED_FUNCTION (decl)) = 1;
-
/* Mark enumeration types as used. */
if (TREE_CODE (decl) == CONST_DECL)
used_types_insert (DECL_CONTEXT (decl));
if (TREE_CODE (decl) == FUNCTION_DECL)
- maybe_instantiate_noexcept (decl);
-
- if (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_DELETED_FN (decl))
{
- if (DECL_ARTIFICIAL (decl))
+ maybe_instantiate_noexcept (decl);
+
+ if (!DECL_DELETED_FN (decl) && DECL_CLONED_FUNCTION_P (decl))
+ TREE_USED (DECL_CLONED_FUNCTION (decl)) = 1;
+
+ if (DECL_DELETED_FN (decl)
+ && !(DECL_BASE_UNDELETED_FN (decl)
+ && DECL_BASE_CONSTRUCTOR_P (decl)))
{
- if (DECL_OVERLOADED_OPERATOR_P (decl) == TYPE_EXPR
+ if (DECL_ARTIFICIAL (decl)
+ &&DECL_OVERLOADED_OPERATOR_P (decl) == TYPE_EXPR
&& LAMBDA_TYPE_P (DECL_CONTEXT (decl)))
+ /* We mark a lambda conversion op as deleted if we
+ can't generate it properly; see
+ maybe_add_lambda_conv_op. */
+ sorry ("converting lambda that uses %<...%> to "
+ "function pointer");
+ else if (complain & tf_error)
{
- /* We mark a lambda conversion op as deleted if we can't
- generate it properly; see maybe_add_lambda_conv_op. */
- sorry ("converting lambda which uses %<...%> to "
- "function pointer");
- return false;
+ error ("use of deleted function %qD", decl);
+ if (!maybe_explain_implicit_delete (decl))
+ inform (DECL_SOURCE_LOCATION (decl), "declared here");
}
+ return false;
}
- if (complain & tf_error)
- {
- error ("use of deleted function %qD", decl);
- if (!maybe_explain_implicit_delete (decl))
- inform (DECL_SOURCE_LOCATION (decl), "declared here");
- }
- return false;
}
if (TREE_DEPRECATED (decl) && (complain & tf_warning)
Index: cp/init.c
===================================================================
--- cp/init.c (revision 240596)
+++ cp/init.c (working copy)
@@ -1093,7 +1093,7 @@ sort_mem_initializers (tree t, tree mem_
void_type_node for an empty list of arguments. */
void
-emit_mem_initializers (tree mem_inits)
+emit_mem_initializers (tree mem_inits, bool skip_vbases)
{
int flags = LOOKUP_NORMAL;
@@ -1150,9 +1150,7 @@ emit_mem_initializers (tree mem_inits)
}
/* Initialize the base. */
- if (BINFO_VIRTUAL_P (subobject))
- construct_virtual_base (subobject, arguments);
- else
+ if (!BINFO_VIRTUAL_P (subobject))
{
tree base_addr;
@@ -1166,6 +1164,8 @@ emit_mem_initializers (tree mem_inits)
tf_warning_or_error);
expand_cleanup_for_base (subobject, NULL_TREE);
}
+ else if (!skip_vbases)
+ construct_virtual_base (subobject, arguments);
}
in_base_initializer = 0;
Index: cp/method.c
===================================================================
--- cp/method.c (revision 240596)
+++ cp/method.c (working copy)
@@ -49,7 +49,7 @@ enum mangling_flags
};
static void do_build_copy_assign (tree);
-static void do_build_copy_constructor (tree);
+static void do_build_copy_constructor (tree, bool);
static tree make_alias_for_thunk (tree);
/* Called once to initialize method.c. */
@@ -532,7 +532,7 @@ add_one_base_init (tree binfo, tree parm
constructor. */
static void
-do_build_copy_constructor (tree fndecl)
+do_build_copy_constructor (tree fndecl, bool skip_vbases)
{
tree parm = FUNCTION_FIRST_USER_PARM (fndecl);
bool move_p = DECL_MOVE_CONSTRUCTOR_P (fndecl);
@@ -647,7 +647,7 @@ do_build_copy_constructor (tree fndecl)
member_init_list = tree_cons (field, init, member_init_list);
}
- finish_mem_initializers (member_init_list);
+ finish_mem_initializers (member_init_list, skip_vbases);
}
}
@@ -784,6 +784,7 @@ synthesize_method (tree fndecl)
location_t save_input_location = input_location;
int error_count = errorcount;
int warning_count = warningcount + werrorcount;
+ bool skip_vbases = false;
/* Reset the source location, we might have been previously
deferred, and thus have saved where we were first needed. */
@@ -794,7 +795,12 @@ synthesize_method (tree fndecl)
cloned function instead. Doing so will automatically fill in the
body for the clone. */
if (DECL_CLONED_FUNCTION_P (fndecl))
- fndecl = DECL_CLONED_FUNCTION (fndecl);
+ {
+ skip_vbases = (DECL_DELETED_FN (fndecl)
+ && DECL_BASE_UNDELETED_FN (fndecl));
+ if (!skip_vbases)
+ fndecl = DECL_CLONED_FUNCTION (fndecl);
+ }
/* We may be in the middle of deferred access check. Disable
it now. */
@@ -819,9 +825,9 @@ synthesize_method (tree fndecl)
{
tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);
if (arg_chain != void_list_node)
- do_build_copy_constructor (fndecl);
+ do_build_copy_constructor (fndecl, skip_vbases);
else
- finish_mem_initializers (NULL_TREE);
+ finish_mem_initializers (NULL_TREE, skip_vbases);
}
/* If we haven't yet generated the body of the function, just
@@ -1319,16 +1325,20 @@ walk_field_subobs (tree fields, tree fnn
}
}
-/* The caller wants to generate an implicit declaration of SFK for CTYPE
- which is const if relevant and CONST_P is set. If spec_p, trivial_p and
- deleted_p are non-null, set their referent appropriately. If diag is
- true, we're either being called from maybe_explain_implicit_delete to
- give errors, or if constexpr_p is non-null, from
- explain_invalid_constexpr_fn. */
+/* The caller wants to generate an implicit declaration of SFK for
+ CTYPE which is const if relevant and CONST_P is set. If SPEC_P,
+ TRIVIAL_P, DELETED_P, VBASE_DELETED_P or CONSTEXPR_P are non-null,
+ set their referent appropriately. VBASE_DELETED_P is used for
+ virtual bases, which is relevant in C++ 11 where a base ctor
+ doesn't need to care about deleted ctors of virtual bases. If diag
+ is true, we're either being called from
+ maybe_explain_implicit_delete to give errors, or if constexpr_p is
+ non-null, from explain_invalid_constexpr_fn. */
static void
synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
- tree *spec_p, bool *trivial_p, bool *deleted_p,
+ tree *spec_p, bool *trivial_p,
+ bool *deleted_p, bool *vbase_deleted_p,
bool *constexpr_p, bool diag,
tree inherited_base, tree inherited_parms)
{
@@ -1358,6 +1368,9 @@ synthesized_method_walk (tree ctype, spe
*deleted_p = false;
}
+ if (vbase_deleted_p)
+ *vbase_deleted_p = false;
+
ctor_p = false;
assign_p = false;
check_vdtor = false;
@@ -1534,27 +1547,34 @@ synthesized_method_walk (tree ctype, spe
}
vbases = CLASSTYPE_VBASECLASSES (ctype);
- if (vec_safe_is_empty (vbases))
- /* No virtual bases to worry about. */;
- else if (!assign_p)
+ if (!assign_p && !vec_safe_is_empty (vbases))
{
+ /* Check virtual base cdtors. */
if (constexpr_p)
*constexpr_p = false;
+
+ /* Point at deleted_p, if we don't care for the vbase_deleted
+ distinction. */
+ if (!ctor_p || !vbase_deleted_p || cxx_dialect < cxx14
+ || !ABSTRACT_CLASS_TYPE_P (ctype))
+ vbase_deleted_p = deleted_p;
+
FOR_EACH_VEC_ELT (*vbases, i, base_binfo)
{
tree basetype = BINFO_TYPE (base_binfo);
if (copy_arg_p)
argtype = build_stub_type (basetype, quals, move_p);
- rval = locate_fn_flags (base_binfo, fnname, argtype, flags, complain);
+ rval = locate_fn_flags (base_binfo, fnname, argtype,
+ flags, complain);
- process_subob_fn (rval, spec_p, trivial_p, deleted_p,
+ process_subob_fn (rval, spec_p, trivial_p, vbase_deleted_p,
constexpr_p, diag, basetype);
if (ctor_p && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (basetype))
{
rval = locate_fn_flags (base_binfo, complete_dtor_identifier,
NULL_TREE, flags, complain);
process_subob_fn (rval, NULL, NULL,
- deleted_p, NULL, false,
+ vbase_deleted_p, NULL, false,
basetype, /*dtor_from_ctor*/true);
}
}
@@ -1594,7 +1614,7 @@ get_defaulted_eh_spec (tree decl)
bool const_p = CP_TYPE_CONST_P (non_reference (parm_type));
tree spec = empty_except_spec;
synthesized_method_walk (ctype, sfk, const_p, &spec, NULL, NULL,
- NULL, false, DECL_INHERITED_CTOR_BASE (decl),
+ NULL, NULL, false, DECL_INHERITED_CTOR_BASE (decl),
parms);
return spec;
}
@@ -1662,15 +1682,15 @@ maybe_explain_implicit_delete (tree decl
bool deleted_p = false;
tree scope = push_scope (ctype);
- synthesized_method_walk (ctype, sfk, const_p,
- &raises, NULL, &deleted_p, NULL, false,
+ synthesized_method_walk (ctype, sfk, const_p, &raises,
+ NULL, &deleted_p, NULL, NULL, false,
DECL_INHERITED_CTOR_BASE (decl), parms);
if (deleted_p)
{
inform (DECL_SOURCE_LOCATION (decl),
"%q#D is implicitly deleted because the default "
"definition would be ill-formed:", decl);
- synthesized_method_walk (ctype, sfk, const_p,
+ synthesized_method_walk (ctype, sfk, const_p, NULL,
NULL, NULL, NULL, NULL, true,
DECL_INHERITED_CTOR_BASE (decl), parms);
}
@@ -1704,7 +1724,7 @@ explain_implicit_non_constexpr (tree dec
bool dummy;
synthesized_method_walk (DECL_CLASS_CONTEXT (decl),
special_function_p (decl), const_p,
- NULL, NULL, NULL, &dummy, true,
+ NULL, NULL, NULL, NULL, &dummy, true,
DECL_INHERITED_CTOR_BASE (decl),
FUNCTION_FIRST_USER_PARMTYPE (decl));
}
@@ -1720,8 +1740,8 @@ deduce_inheriting_ctor (tree decl)
tree spec;
bool trivial, constexpr_, deleted;
synthesized_method_walk (DECL_CONTEXT (decl), sfk_inheriting_constructor,
- false, &spec, &trivial, &deleted, &constexpr_,
- /*diag*/false,
+ false, &spec, &trivial, &deleted, NULL,
+ &constexpr_, /*diag*/false,
DECL_INHERITED_CTOR_BASE (decl),
FUNCTION_FIRST_USER_PARMTYPE (decl));
DECL_DELETED_FN (decl) = deleted;
@@ -1748,7 +1768,7 @@ implicitly_declare_fn (special_function_
tree this_parm;
tree name;
HOST_WIDE_INT saved_processing_template_decl;
- bool deleted_p;
+ bool deleted_p, vbase_deleted_p;
bool constexpr_p;
/* Because we create declarations for implicitly declared functions
@@ -1841,15 +1861,16 @@ implicitly_declare_fn (special_function_
{
raises = unevaluated_noexcept_spec ();
synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
- &deleted_p, &constexpr_p, false,
- inherited_base, inherited_parms);
+ &deleted_p, &vbase_deleted_p, &constexpr_p,
+ false, inherited_base, inherited_parms);
}
else
synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
- &deleted_p, &constexpr_p, false,
+ &deleted_p, NULL, &constexpr_p, false,
inherited_base, inherited_parms);
+
/* Don't bother marking a deleted constructor as constexpr. */
- if (deleted_p)
+ if (deleted_p || vbase_deleted_p)
constexpr_p = false;
/* A trivial copy/move constructor is also a constexpr constructor,
unless the class has virtual bases (7.1.5p4). */
@@ -1929,8 +1950,16 @@ implicitly_declare_fn (special_function_
DECL_DEFAULTED_FN (fn) = 1;
if (cxx_dialect >= cxx11)
{
- DECL_DELETED_FN (fn) = deleted_p;
+ if (!deleted_p && vbase_deleted_p)
+ {
+ /* The general ctor pattern is deleted, but the instance for
+ a base is not (because the cause of the deletion is a
+ missing vbase ctor). */
+ DECL_BASE_UNDELETED_FN (fn) = true;
+ deleted_p = true;
+ }
DECL_DECLARED_CONSTEXPR_P (fn) = constexpr_p;
+ DECL_DELETED_FN (fn) = deleted_p;
}
DECL_EXTERNAL (fn) = true;
DECL_NOT_REALLY_EXTERN (fn) = 1;
@@ -1962,7 +1991,7 @@ implicitly_declare_fn (special_function_
location_t loc = input_location;
input_location = DECL_SOURCE_LOCATION (fn);
synthesized_method_walk (type, kind, const_p,
- NULL, NULL, NULL, NULL, true,
+ NULL, NULL, NULL, NULL, NULL, true,
NULL_TREE, NULL_TREE);
input_location = loc;
}
Index: cp/parser.c
===================================================================
--- cp/parser.c (revision 240596)
+++ cp/parser.c (working copy)
@@ -13648,7 +13648,7 @@ cp_parser_ctor_initializer_opt (cp_parse
{
/* Do default initialization of any bases and members. */
if (DECL_CONSTRUCTOR_P (current_function_decl))
- finish_mem_initializers (NULL_TREE);
+ finish_mem_initializers (NULL_TREE, false);
return false;
}
@@ -13745,7 +13745,7 @@ cp_parser_mem_initializer_list (cp_parse
/* Perform semantic analysis. */
if (DECL_CONSTRUCTOR_P (current_function_decl))
- finish_mem_initializers (mem_initializer_list);
+ finish_mem_initializers (mem_initializer_list, false);
}
/* Parse a mem-initializer.
Index: cp/pt.c
===================================================================
--- cp/pt.c (revision 240596)
+++ cp/pt.c (working copy)
@@ -15231,7 +15231,7 @@ tsubst_expr (tree t, tree args, tsubst_f
case CTOR_INITIALIZER:
finish_mem_initializers (tsubst_initializer_list
- (TREE_OPERAND (t, 0), args));
+ (TREE_OPERAND (t, 0), args), false);
break;
case RETURN_EXPR:
Index: cp/semantics.c
===================================================================
--- cp/semantics.c (revision 240596)
+++ cp/semantics.c (working copy)
@@ -1605,7 +1605,7 @@ finish_eh_cleanup (tree cleanup)
emit_mem_initializers. */
void
-finish_mem_initializers (tree mem_inits)
+finish_mem_initializers (tree mem_inits, bool skip_vbases)
{
/* Reorder the MEM_INITS so that they are in the order they appeared
in the source program. */
@@ -1631,7 +1631,7 @@ finish_mem_initializers (tree mem_inits)
CTOR_INITIALIZER, mem_inits));
}
else
- emit_mem_initializers (mem_inits);
+ emit_mem_initializers (mem_inits, skip_vbases);
}
/* Obfuscate EXPR if it looks like an id-expression or member access so
Index: testsuite/g++.dg/cpp0x/pr66443-cxx11.C
===================================================================
--- testsuite/g++.dg/cpp0x/pr66443-cxx11.C (nonexistent)
+++ testsuite/g++.dg/cpp0x/pr66443-cxx11.C (working copy)
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++11_only } }
+
+// pr c++/66443 it is still ill-formed in C++ 11 for a synthesized
+// ctor that's deleted only because of virtual base construction
+
+static bool a_made;
+
+struct A { // { dg-message "candidate" }
+ A( int ) { a_made = true; } // { dg-message "candidate" }
+};
+
+struct B: virtual A { // { dg-message "no matching function" }
+ int m;
+ virtual void Frob () = 0;
+};
+
+class C: public B {
+public:
+ C();
+ virtual void Frob ();
+};
+
+void C::Frob ()
+{
+}
+
+C::C ()
+ : A( 1 ) // { dg-error "deleted function" }
+{ }
+
Index: testsuite/g++.dg/cpp1y/pr66443-cxx14.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr66443-cxx14.C (nonexistent)
+++ testsuite/g++.dg/cpp1y/pr66443-cxx14.C (working copy)
@@ -0,0 +1,47 @@
+// { dg-do run { target c++14 } }
+
+// pr c++/66443 a synthesized ctor of an abstract class that's deleted
+// only because of virtual base construction doesn't stop a derived
+// class using it as a base object constructor (provided it has a
+// suitable ctor invocation of the virtual base).
+
+static int a_made;
+
+struct A {
+ A *m_a = this;
+ A (int) { a_made++; }
+};
+
+struct B : virtual A {
+ A *m_b = this;
+ virtual bool Ok () = 0; // abstract
+};
+
+struct C : B {
+ // C::m_c is placed where a complete B object would put A
+ int m_c = 1729;
+public:
+ C();
+ virtual bool Ok ();
+};
+
+bool C::Ok ()
+{
+ // check everyone agreed on where A is
+ return a_made == 1 && m_a == this && m_b == this && m_c == 1729;
+}
+
+C::C ()
+ : A (1) // Explicit call of A's ctor
+{ }
+
+bool Ok (C &c)
+{
+}
+
+int main ()
+{
+ C c;
+
+ return !c.Ok ();
+}
Index: testsuite/g++.dg/cpp1y/pr66443-cxx14-2.C
===================================================================
--- testsuite/g++.dg/cpp1y/pr66443-cxx14-2.C (nonexistent)
+++ testsuite/g++.dg/cpp1y/pr66443-cxx14-2.C (working copy)
@@ -0,0 +1,29 @@
+// { dg-do compile { target c++14 } }
+
+// pr c++/66443 a synthesized ctor of an abstract class that's deleted
+// only because of virtual base construction doesn't stop a derived
+// class using it as a base object constructor (provided it has a
+// suitable ctor invocation of the virtual base).
+
+// However we should still complain if the intermediate base is a
+// non-abstract type.
+
+static int a_made;
+
+struct A {
+ A *m_a = this;
+ A (int) { a_made++; }
+};
+
+struct B : virtual A { // { dg-error "no matching function" }
+ A *m_b = this;
+ virtual bool Ok (); // not abstract
+};
+
+bool B::Ok ()
+{
+ return false;
+}
+
+
+B b; // { dg-error "deleted" }