diff mbox

[C++/66443] deleted ctor and vbase construction

Message ID 362b3538-6219-ded5-1588-5baa113919ae@acm.org
State New
Headers show

Commit Message

Nathan Sidwell Sept. 30, 2016, 5:39 p.m. UTC
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.

Comments

Jason Merrill Oct. 5, 2016, 11:49 p.m. UTC | #1
On Fri, Sep 30, 2016 at 1:39 PM, Nathan Sidwell <nathan@acm.org> wrote:
> 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()

But DR 1658 says that B::B is *not* deleted (because A is not a
potentially constructed subobject).  Implementing that might be
simpler than trying to have a deleted complete and non-deleted base
constructor variant.

Jason
Nathan Sidwell Oct. 6, 2016, 10:26 a.m. UTC | #2
On 10/05/16 19:49, Jason Merrill wrote:

> But DR 1658 says that B::B is *not* deleted (because A is not a
> potentially constructed subobject).

oo, I'd not noticed that.  It certainly might make things much simpler. 
Essentially a check for ABSTRACT_TYPE_P somewhere in that code.

nathan
diff mbox

Patch

2016-09-30  Nathan Sidwell  <nathan@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" }