diff mbox

Sequence of C++ PATCHes to C++11 reference semantics

Message ID 4E6E4877.6080607@redhat.com
State New
Headers show

Commit Message

Jason Merrill Sept. 12, 2011, 5:59 p.m. UTC
While debugging a template deduction change that I didn't end up making, 
I fell down a rabbit hole into issues with our handling of C++11 
reference semantics.  Most of these changes are less necessary without 
the deduction change, but there is still a minor bug fix here, and I've 
been meaning to clean up this stuff for a while now.

1) reference_binding wasn't handling xvalues (what you get from e.g. a 
function returning an rvalue reference) properly.  I fixed 
LOOKUP_NO_TEMP_BIND to not apply to xvalues, and fixed rvalue reference 
binding to respect it.  Then I realized that the rules for ?: 
conversions are stricter, requiring actual lvalue/lvalue match rather 
than just direct binding, so I had to add LOOKUP_NO_RVAL_BIND as well.

2) For some reason we weren't passing the LOOKUP flags down through the 
reference binding functions.  This change isn't necessary without the 
deduction change, but seems correct.

3) We had two separate functions doing the same thing: 
build_user_type_conversion_1 and convert_class_to_reference_1.  This led 
to bugs from changes being made in one place and not the other.  I've 
fixed this by discarding the reference-specific function.  This change 
required change #1 above.

4) As a result of #3 we started rejecting explicit conversion functions 
that would require a further conversion of greater than exact match rank 
to the reference type being initialized.  This seems correct to me even 
though the standard doesn't say that yet.  I had to change some things 
around a bit to get a helpful error message for this case.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox

Patch

commit 68b5d5b940586209465340a1a6f20d80db2f1c8a
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Sep 9 15:12:59 2011 -0400

    	* cp-tree.h (LOOKUP_NO_RVAL_BIND): New.
    	* call.c (conditional_conversion): Use it.
    	(reference_binding): Fix handling of xvalues.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 62bee2d..81c8a90 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1564,7 +1564,8 @@  reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags)
   tree tfrom;
   bool related_p;
   bool compatible_p;
-  cp_lvalue_kind is_lvalue = clk_none;
+  cp_lvalue_kind gl_kind;
+  bool is_lvalue;
 
   if (TREE_CODE (to) == FUNCTION_TYPE && expr && type_unknown_p (expr))
     {
@@ -1574,14 +1575,6 @@  reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags)
       from = TREE_TYPE (expr);
     }
 
-  if (TREE_CODE (from) == REFERENCE_TYPE)
-    {
-      from = TREE_TYPE (from);
-      if (!TYPE_REF_IS_RVALUE (rfrom)
-	  || TREE_CODE (from) == FUNCTION_TYPE)
-	is_lvalue = clk_ordinary;
-    }
-
   if (expr && BRACE_ENCLOSED_INITIALIZER_P (expr))
     {
       maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
@@ -1597,11 +1590,28 @@  reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags)
 	}
     }
 
-  if (is_lvalue == clk_none && expr)
-    is_lvalue = real_lvalue_p (expr);
+  if (TREE_CODE (from) == REFERENCE_TYPE)
+    {
+      from = TREE_TYPE (from);
+      if (!TYPE_REF_IS_RVALUE (rfrom)
+	  || TREE_CODE (from) == FUNCTION_TYPE)
+	gl_kind = clk_ordinary;
+      else
+	gl_kind = clk_rvalueref;
+    }
+  else if (expr)
+    {
+      gl_kind = lvalue_kind (expr);
+      if (gl_kind & clk_class)
+	/* A class prvalue is not a glvalue.  */
+	gl_kind = clk_none;
+    }
+  else
+    gl_kind = clk_none;
+  is_lvalue = gl_kind && !(gl_kind & clk_rvalueref);
 
   tfrom = from;
-  if ((is_lvalue & clk_bitfield) != 0)
+  if ((gl_kind & clk_bitfield) != 0)
     tfrom = unlowered_expr_type (expr);
 
   /* Figure out whether or not the types are reference-related and
@@ -1619,16 +1629,16 @@  reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags)
      the reference and expression is an lvalue. In DR391, the wording in
      [8.5.3/5 dcl.init.ref] is changed to also require direct bindings for
      const and rvalue references to rvalues of compatible class type.
-     We should also do direct bindings for non-class "rvalues" derived from
-     rvalue references.  */
+     We should also do direct bindings for non-class xvalues.  */
   if (compatible_p
       && (is_lvalue
 	  || (((CP_TYPE_CONST_NON_VOLATILE_P (to)
-		&& !(flags & LOOKUP_NO_TEMP_BIND))
+		&& !(flags & LOOKUP_NO_RVAL_BIND))
 	       || TYPE_REF_IS_RVALUE (rto))
-	      && (CLASS_TYPE_P (from)
-		  || TREE_CODE (from) == ARRAY_TYPE
-		  || (expr && lvalue_p (expr))))))
+	      && (gl_kind
+		  || (!(flags & LOOKUP_NO_TEMP_BIND)
+		      && (CLASS_TYPE_P (from)
+			  || TREE_CODE (from) == ARRAY_TYPE))))))
     {
       /* [dcl.init.ref]
 
@@ -1661,8 +1671,8 @@  reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags)
 	conv->rvaluedness_matches_p 
           = (TYPE_REF_IS_RVALUE (rto) == !is_lvalue);
 
-      if ((is_lvalue & clk_bitfield) != 0
-	  || ((is_lvalue & clk_packed) != 0 && !TYPE_PACKED (to)))
+      if ((gl_kind & clk_bitfield) != 0
+	  || ((gl_kind & clk_packed) != 0 && !TYPE_PACKED (to)))
 	/* For the purposes of overload resolution, we ignore the fact
 	   this expression is a bitfield or packed field. (In particular,
 	   [over.ics.ref] says specifically that a function with a
@@ -3575,8 +3585,8 @@  build_user_type_conversion_1 (tree totype, tree expr, int flags)
       struct z_candidate *old_candidates;
 
       /* If we are called to convert to a reference type, we are trying to
-	 find an lvalue binding, so don't even consider temporaries.  If
-	 we don't find an lvalue binding, the caller will try again to
+	 find a direct binding, so don't even consider temporaries.  If
+	 we don't find a direct binding, the caller will try again to
 	 look for a temporary binding.  */
       if (TREE_CODE (totype) == REFERENCE_TYPE)
 	convflags |= LOOKUP_NO_TEMP_BIND;
@@ -4297,16 +4307,17 @@  conditional_conversion (tree e1, tree e2)
   /* [expr.cond]
 
      If E2 is an lvalue: E1 can be converted to match E2 if E1 can be
-     implicitly converted (clause _conv_) to the type "reference to
+     implicitly converted (clause _conv_) to the type "lvalue reference to
      T2", subject to the constraint that in the conversion the
-     reference must bind directly (_dcl.init.ref_) to E1.  */
+     reference must bind directly (_dcl.init.ref_) to an lvalue.  */
   if (real_lvalue_p (e2))
     {
       conv = implicit_conversion (build_reference_type (t2),
 				  t1,
 				  e1,
 				  /*c_cast_p=*/false,
-				  LOOKUP_NO_TEMP_BIND|LOOKUP_ONLYCONVERTING);
+				  LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND
+				  |LOOKUP_ONLYCONVERTING);
       if (conv)
 	return conv;
     }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ae4cd07..06572fd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4294,6 +4294,8 @@  enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
 #define LOOKUP_ALREADY_DIGESTED (LOOKUP_DEFAULTED << 1)
 /* An instantiation with explicit template arguments.  */
 #define LOOKUP_EXPLICIT_TMPL_ARGS (LOOKUP_ALREADY_DIGESTED << 1)
+/* Like LOOKUP_NO_TEMP_BIND, but also prevent binding to xvalues.  */
+#define LOOKUP_NO_RVAL_BIND (LOOKUP_EXPLICIT_TMPL_ARGS << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))

commit 20774bd76474db43899cebe047edb11363081257
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Sep 9 15:53:47 2011 -0400

    	* call.c (initialize_reference): Add flags parm.
    	* decl.c (grok_reference_init): Likewise.
    	(check_initializer): Pass it.
    	* typeck.c (convert_for_initialization): Likewise.
    	* cp-tree.h: Adjust.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 81c8a90..7912fad 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -8717,7 +8717,7 @@  set_up_extended_ref_temp (tree decl, tree expr, tree *cleanup, tree *initp)
 
 tree
 initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
-		      tsubst_flags_t complain)
+		      int flags, tsubst_flags_t complain)
 {
   conversion *conv;
   void *p;
@@ -8729,7 +8729,7 @@  initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
   p = conversion_obstack_alloc (0);
 
   conv = reference_binding (type, TREE_TYPE (expr), expr, /*c_cast_p=*/false,
-			    LOOKUP_NORMAL);
+			    flags);
   if (!conv || conv->bad_p)
     {
       if (complain & tf_error)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 06572fd..12a2895 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4757,7 +4757,8 @@  extern tree type_passed_as			(tree);
 extern tree convert_for_arg_passing		(tree, tree);
 extern bool is_properly_derived_from		(tree, tree);
 extern tree set_up_extended_ref_temp		(tree, tree, tree *, tree *);
-extern tree initialize_reference		(tree, tree, tree, tree *, tsubst_flags_t);
+extern tree initialize_reference		(tree, tree, tree, tree *, int,
+						 tsubst_flags_t);
 extern tree make_temporary_var_for_ref_to_temp	(tree, tree);
 extern tree strip_top_quals			(tree);
 extern bool reference_related_p			(tree, tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index a68bcb1..dc49eb2 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -71,7 +71,7 @@  static void require_complete_types_for_parms (tree);
 static int ambi_op_p (enum tree_code);
 static int unary_op_p (enum tree_code);
 static void push_local_name (tree);
-static tree grok_reference_init (tree, tree, tree, tree *);
+static tree grok_reference_init (tree, tree, tree, tree *, int);
 static tree grokvardecl (tree, tree, const cp_decl_specifier_seq *,
 			 int, int, tree);
 static int check_static_variable_definition (tree, tree);
@@ -4574,7 +4574,8 @@  start_decl_1 (tree decl, bool initialized)
    Quotes on semantics can be found in ARM 8.4.3.  */
 
 static tree
-grok_reference_init (tree decl, tree type, tree init, tree *cleanup)
+grok_reference_init (tree decl, tree type, tree init, tree *cleanup,
+		     int flags)
 {
   tree tmp;
 
@@ -4603,7 +4604,8 @@  grok_reference_init (tree decl, tree type, tree init, tree *cleanup)
      DECL_INITIAL for local references (instead assigning to them
      explicitly); we need to allow the temporary to be initialized
      first.  */
-  tmp = initialize_reference (type, init, decl, cleanup, tf_warning_or_error);
+  tmp = initialize_reference (type, init, decl, cleanup, flags,
+			      tf_warning_or_error);
   if (DECL_DECLARED_CONSTEXPR_P (decl))
     {
       tmp = cxx_constant_value (tmp);
@@ -5468,7 +5470,7 @@  check_initializer (tree decl, tree init, int flags, tree *cleanup)
   else if (!init && DECL_REALLY_EXTERN (decl))
     ;
   else if (TREE_CODE (type) == REFERENCE_TYPE)
-    init = grok_reference_init (decl, type, init, cleanup);
+    init = grok_reference_init (decl, type, init, cleanup, flags);
   else if (init || type_build_ctor_call (type))
     {
       if (!init)
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 9db0ce3..ed52f3c 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -7510,7 +7510,7 @@  convert_for_initialization (tree exp, tree type, tree rhs, int flags,
       if (fndecl)
 	savew = warningcount, savee = errorcount;
       rhs = initialize_reference (type, rhs, /*decl=*/NULL_TREE,
-				  /*cleanup=*/NULL, complain);
+				  /*cleanup=*/NULL, flags, complain);
       if (fndecl)
 	{
 	  if (warningcount > savew)

commit c966b47f3e80c6089c3767006eb7a0c89ae2d2fa
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Sep 9 15:13:57 2011 -0400

    	* call.c (convert_class_to_reference)
    	(convert_class_to_reference_1): Remove.
    	(reference_binding): Use build_user_type_conversion_1 instead.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 7912fad..d58ed13 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -202,7 +202,6 @@  static struct z_candidate *add_candidate
 static tree source_type (conversion *);
 static void add_warning (struct z_candidate *, struct z_candidate *);
 static bool reference_compatible_p (tree, tree);
-static conversion *convert_class_to_reference (tree, tree, tree, int);
 static conversion *direct_reference_binding (tree, conversion *);
 static bool promoted_arithmetic_type_p (tree);
 static conversion *conditional_conversion (tree, tree);
@@ -1352,160 +1351,6 @@  reference_compatible_p (tree t1, tree t2)
 	  && at_least_as_qualified_p (t1, t2));
 }
 
-/* Determine whether or not the EXPR (of class type S) can be
-   converted to T as in [over.match.ref].  */
-
-static conversion *
-convert_class_to_reference_1 (tree reference_type, tree s, tree expr, int flags)
-{
-  tree conversions;
-  tree first_arg;
-  conversion *conv;
-  tree t;
-  struct z_candidate *candidates;
-  struct z_candidate *cand;
-  bool any_viable_p;
-
-  if (!expr)
-    return NULL;
-
-  conversions = lookup_conversions (s);
-  if (!conversions)
-    return NULL;
-
-  /* [over.match.ref]
-
-     Assuming that "cv1 T" is the underlying type of the reference
-     being initialized, and "cv S" is the type of the initializer
-     expression, with S a class type, the candidate functions are
-     selected as follows:
-
-     --The conversion functions of S and its base classes are
-       considered.  Those that are not hidden within S and yield type
-       "reference to cv2 T2", where "cv1 T" is reference-compatible
-       (_dcl.init.ref_) with "cv2 T2", are candidate functions.
-
-     The argument list has one argument, which is the initializer
-     expression.  */
-
-  candidates = 0;
-
-  /* Conceptually, we should take the address of EXPR and put it in
-     the argument list.  Unfortunately, however, that can result in
-     error messages, which we should not issue now because we are just
-     trying to find a conversion operator.  Therefore, we use NULL,
-     cast to the appropriate type.  */
-  first_arg = build_int_cst (build_pointer_type (s), 0);
-
-  t = TREE_TYPE (reference_type);
-
-  /* We're performing a user-defined conversion to a desired type, so set
-     this for the benefit of add_candidates.  */
-  flags |= LOOKUP_NO_CONVERSION;
-
-  for (; conversions; conversions = TREE_CHAIN (conversions))
-    {
-      tree fns = TREE_VALUE (conversions);
-      tree binfo = TREE_PURPOSE (conversions);
-      struct z_candidate *old_candidates = candidates;;
-
-      add_candidates (fns, first_arg, NULL, reference_type,
-		      NULL_TREE, false,
-		      binfo, TYPE_BINFO (s),
-		      flags, &candidates);
-
-      for (cand = candidates; cand != old_candidates; cand = cand->next)
-	{
-	  /* Now, see if the conversion function really returns
-	     an lvalue of the appropriate type.  From the
-	     point of view of unification, simply returning an
-	     rvalue of the right type is good enough.  */
-	  tree f = cand->fn;
-	  tree t2 = TREE_TYPE (TREE_TYPE (f));
-	  if (cand->viable == 0)
-	    /* Don't bother looking more closely.  */;
-	  else if (TREE_CODE (t2) != REFERENCE_TYPE
-		   || !reference_compatible_p (t, TREE_TYPE (t2)))
-	    {
-	      /* No need to set cand->reason here; this is most likely
-		 an ambiguous match.  If it's not, either this candidate
-		 will win, or we will have identified a reason for it
-		 losing already.  */
-	      cand->viable = 0;
-	    }
-	  else
-	    {
-	      conversion *identity_conv;
-	      /* Build a standard conversion sequence indicating the
-		 binding from the reference type returned by the
-		 function to the desired REFERENCE_TYPE.  */
-	      identity_conv
-		= build_identity_conv (TREE_TYPE (TREE_TYPE
-						  (TREE_TYPE (cand->fn))),
-				       NULL_TREE);
-	      cand->second_conv
-		= (direct_reference_binding
-		   (reference_type, identity_conv));
-	      cand->second_conv->rvaluedness_matches_p
-		= TYPE_REF_IS_RVALUE (TREE_TYPE (TREE_TYPE (cand->fn)))
-		  == TYPE_REF_IS_RVALUE (reference_type);
-	      cand->second_conv->bad_p |= cand->convs[0]->bad_p;
-
-              /* Don't allow binding of lvalues to rvalue references.  */
-              if (TYPE_REF_IS_RVALUE (reference_type)
-                  /* Function lvalues are OK, though.  */
-                  && TREE_CODE (TREE_TYPE (reference_type)) != FUNCTION_TYPE
-                  && !TYPE_REF_IS_RVALUE (TREE_TYPE (TREE_TYPE (cand->fn))))
-                cand->second_conv->bad_p = true;
-	    }
-	}
-    }
-
-  candidates = splice_viable (candidates, pedantic, &any_viable_p);
-  /* If none of the conversion functions worked out, let our caller
-     know.  */
-  if (!any_viable_p)
-    return NULL;
-
-  cand = tourney (candidates);
-  if (!cand)
-    return NULL;
-
-  /* Now that we know that this is the function we're going to use fix
-     the dummy first argument.  */
-  gcc_assert (cand->first_arg == NULL_TREE
-	      || integer_zerop (cand->first_arg));
-  cand->first_arg = build_this (expr);
-
-  /* Build a user-defined conversion sequence representing the
-     conversion.  */
-  conv = build_conv (ck_user,
-		     TREE_TYPE (TREE_TYPE (cand->fn)),
-		     build_identity_conv (TREE_TYPE (expr), expr));
-  conv->cand = cand;
-
-  if (cand->viable == -1)
-    conv->bad_p = true;
-
-  /* Merge it with the standard conversion sequence from the
-     conversion function's return type to the desired type.  */
-  cand->second_conv = merge_conversion_sequences (conv, cand->second_conv);
-
-  return cand->second_conv;
-}
-
-/* Wrapper for above.  */
-
-static conversion *
-convert_class_to_reference (tree reference_type, tree s, tree expr, int flags)
-{
-  conversion *ret;
-  bool subtime = timevar_cond_start (TV_OVERLOAD);
-  ret = convert_class_to_reference_1 (reference_type, s, expr, flags);
-  timevar_cond_stop (TV_OVERLOAD, subtime);
-  return ret;
-}
-
 /* A reference of the indicated TYPE is being bound directly to the
    expression represented by the implicit conversion sequence CONV.
    Return a conversion sequence for this binding.  */
@@ -1715,9 +1560,9 @@  reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags)
 
 	the reference is bound to the lvalue result of the conversion
 	in the second case.  */
-      conv = convert_class_to_reference (rto, from, expr, flags);
-      if (conv)
-	return conv;
+      z_candidate *cand = build_user_type_conversion_1 (rto, expr, flags);
+      if (cand)
+	return cand->second_conv;
     }
 
   /* From this point on, we conceptually need temporaries, even if we
@@ -3477,7 +3322,7 @@  add_list_candidates (tree fns, tree first_arg,
 /* Returns the best overload candidate to perform the requested
    conversion.  This function is used for three the overloading situations
    described in [over.match.copy], [over.match.conv], and [over.match.ref].
-   If TOTYPE is a REFERENCE_TYPE, we're trying to find an lvalue binding as
+   If TOTYPE is a REFERENCE_TYPE, we're trying to find a direct binding as
    per [dcl.init.ref], so we ignore temporary bindings.  */
 
 static struct z_candidate *
@@ -3636,6 +3481,8 @@  build_user_type_conversion_1 (tree totype, tree expr, int flags)
 		 yield type T or a type that can be converted to type T
 		 with a qualification conversion (4.4) are also candidate
 		 functions.  */
+	      /* 13.3.1.6 doesn't have a parallel restriction, but it should;
+		 I've raised this issue with the committee. --jason 9/2011 */
 	      cand->viable = -1;
 	      cand->reason = explicit_conversion_rejection (rettype, totype);
 	    }
diff --git a/libstdc++-v3/testsuite/20_util/is_constructible/value-2.cc b/libstdc++-v3/testsuite/20_util/is_constructible/value-2.cc
index 9e4bd97..24fde93 100644
--- a/libstdc++-v3/testsuite/20_util/is_constructible/value-2.cc
+++ b/libstdc++-v3/testsuite/20_util/is_constructible/value-2.cc
@@ -236,8 +236,8 @@  static_assert(std::is_constructible<const int&,
 static_assert(std::is_constructible<const int&,
 	      ExplicitTo<int&>>::value, "Error");
 
-static_assert(std::is_constructible<B&&, ExplicitTo<D&&>>::value, "Error");
-static_assert(std::is_constructible<B&&, ExplicitTo<D&&>&>::value, "Error");
+static_assert(!std::is_constructible<B&&, ExplicitTo<D&&>>::value, "Error");
+static_assert(!std::is_constructible<B&&, ExplicitTo<D&&>&>::value, "Error");
 
 static_assert(!std::is_constructible<B&, B&&>::value, "Error");
 static_assert(!std::is_constructible<D&, B&&>::value, "Error");

commit b3e1e7675e6835b4cb0690ff994435e9096e016b
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Sep 9 17:49:09 2011 -0400

    	* call.c (merge_conversion_sequences): Set bad_p and user_conv_p
    	on all of the second conversion sequence.
    	(build_user_type_conversion_1): Set bad_p on the ck_user conv.
    	(convert_like_real): Handle bad ck_ref_bind with user_conv_p in the
    	first section.  Fix loop logic.
    	(initialize_reference): Call convert_like for diagnostics when
    	we have a (bad) conversion.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index d58ed13..a97e8c7 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3242,21 +3242,23 @@  static conversion *
 merge_conversion_sequences (conversion *user_seq, conversion *std_seq)
 {
   conversion **t;
+  bool bad = user_seq->bad_p;
 
   gcc_assert (user_seq->kind == ck_user);
 
   /* Find the end of the second conversion sequence.  */
-  t = &(std_seq);
-  while ((*t)->kind != ck_identity)
-    t = &((*t)->u.next);
+  for (t = &std_seq; (*t)->kind != ck_identity; t = &((*t)->u.next))
+    {
+      /* The entire sequence is a user-conversion sequence.  */
+      (*t)->user_conv_p = true;
+      if (bad)
+	(*t)->bad_p = true;
+    }
 
   /* Replace the identity conversion with the user conversion
      sequence.  */
   *t = user_seq;
 
-  /* The entire sequence is a user-conversion sequence.  */
-  std_seq->user_conv_p = true;
-
   return std_seq;
 }
 
@@ -3533,6 +3535,8 @@  build_user_type_conversion_1 (tree totype, tree expr, int flags)
       ? totype : non_reference (TREE_TYPE (TREE_TYPE (cand->fn)))),
      build_identity_conv (TREE_TYPE (expr), expr));
   conv->cand = cand;
+  if (cand->viable == -1)
+    conv->bad_p = true;
 
   /* Remember that this was a list-initialization.  */
   if (flags & LOOKUP_NO_NARROWING)
@@ -3542,9 +3546,6 @@  build_user_type_conversion_1 (tree totype, tree expr, int flags)
   cand->second_conv = merge_conversion_sequences (conv,
 						  cand->second_conv);
 
-  if (cand->viable == -1)
-    cand->second_conv->bad_p = true;
-
   return cand;
 }
 
@@ -5529,7 +5530,8 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
       && convs->kind != ck_user
       && convs->kind != ck_list
       && convs->kind != ck_ambig
-      && convs->kind != ck_ref_bind
+      && (convs->kind != ck_ref_bind
+	  || convs->user_conv_p)
       && convs->kind != ck_rvalue
       && convs->kind != ck_base)
     {
@@ -5542,7 +5544,7 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  && BRACE_ENCLOSED_INITIALIZER_P (CONSTRUCTOR_ELT (expr, 0)->value))
 	permerror (input_location, "too many braces around initializer for %qT", totype);
 
-      for (; t; t = convs->u.next)
+      for (; t; t = t->u.next)
 	{
 	  if (t->kind == ck_user && t->cand->reason)
 	    {
@@ -5553,7 +5555,11 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 					/*issue_conversion_warnings=*/false,
 					/*c_cast_p=*/false,
 					complain);
-	      return cp_convert (totype, expr);
+	      if (convs->kind == ck_ref_bind)
+		return convert_to_reference (totype, expr, CONV_IMPLICIT,
+					     LOOKUP_NORMAL, NULL_TREE);
+	      else
+		return cp_convert (totype, expr);
 	    }
 	  else if (t->kind == ck_user || !t->bad_p)
 	    {
@@ -5788,9 +5794,11 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
       {
 	tree ref_type = totype;
 
-	if (convs->bad_p && TYPE_REF_IS_RVALUE (ref_type)
-	    && real_lvalue_p (expr))
+	if (convs->bad_p && !convs->u.next->bad_p)
 	  {
+	    gcc_assert (TYPE_REF_IS_RVALUE (ref_type)
+			&& real_lvalue_p (expr));
+
 	    error ("cannot bind %qT lvalue to %qT",
 		   TREE_TYPE (expr), totype);
 	    if (fn)
@@ -8581,9 +8589,11 @@  initialize_reference (tree type, tree expr, tree decl, tree *cleanup,
     {
       if (complain & tf_error)
 	{
-	  if (!CP_TYPE_CONST_P (TREE_TYPE (type))
-	      && !TYPE_REF_IS_RVALUE (type)
-	      && !real_lvalue_p (expr))
+	  if (conv)
+	    convert_like (conv, expr, complain);
+	  else if (!CP_TYPE_CONST_P (TREE_TYPE (type))
+		   && !TYPE_REF_IS_RVALUE (type)
+		   && !real_lvalue_p (expr))
 	    error ("invalid initialization of non-const reference of "
 		   "type %qT from an rvalue of type %qT",
 		   type, TREE_TYPE (expr));
diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit7.C b/gcc/testsuite/g++.dg/cpp0x/explicit7.C
new file mode 100644
index 0000000..7a0b73e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -0,0 +1,17 @@ 
+// [over.match.conv]: For direct-initialization, those explicit conversion
+// functions that are not hidden within S and yield type T or a type that
+// can be converted to type T with a qualification conversion (4.4) are
+// also candidate functions.
+
+// { dg-options -std=c++0x }
+
+struct A { };
+struct B: A { };
+struct C {
+  explicit operator B*();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" }
+};
+
+C c;
+A* ap (c);			// { dg-error "" }
+A& ar (c);			// { dg-error "" }