[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gcc-patches
Subject:    [PATCH 1/3] c++: expr-cast - C-style cast conformance [PR77465]
From:       "Ed Catmur" <ed () catmur ! uk>
Date:       2021-12-30 19:57:25
Message-ID: b7066d62-7b33-4536-bf69-6f5bcf4664cf () www ! fastmail ! com
[Download RAW message or body]

PR c++/77465 - [DR909] rejected C-style cast involving casting away constness from \
result of conversion operator

        PR c++/77465

gcc/cp/ChangeLog:

        * call.c (tourney):
        (joust):
        (build_user_type_conversion_1):
        (reference_binding):
        (implicit_conversion_1):
        (build_user_type_conversion):
        (perform_overload_resolution):
        (build_op_call):
        (build_conditional_expr):
        (build_new_op):
        (build_op_subscript):
        (convert_like_internal):
        (build_over_call):
        (build_new_method_call):
        * cp-tree.h (build_user_type_conversion):

gcc/testsuite/ChangeLog:

        * g++.old-deja/g++.brendan/misc17.C:
        * g++.old-deja/g++.mike/p2855.C:
        * g++.dg/conversion/cwg909.C: New test.


["0001-Apply-C-style-cast-rules-for-user-defined-conversion.patch" (0001-Apply-C-style-cast-rules-for-user-defined-conversion.patch)]

From 42d65d8dd984ed7f41789546cc8eeefc82cbb3fe Mon Sep 17 00:00:00 2001
From: Ed Catmur <ed@catmur.uk>
Date: Mon, 20 Dec 2021 01:06:38 +0000
Subject: [PATCH 1/3] Apply C-style cast rules for user-defined conversions

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#909
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77465
---
 gcc/cp/call.c                                 | 75 ++++++++++++-------
 gcc/cp/cp-tree.h                              |  2 +-
 gcc/testsuite/g++.dg/conversion/cwg909.C      | 11 +++
 .../g++.old-deja/g++.brendan/misc17.C         |  4 +-
 gcc/testsuite/g++.old-deja/g++.mike/p2855.C   |  3 +-
 5 files changed, 63 insertions(+), 32 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/conversion/cwg909.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index bee367f57d7..f7aad03d4d0 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -153,10 +153,11 @@ static struct obstack conversion_obstack;
 static bool conversion_obstack_initialized;
 struct rejection_reason;
 
-static struct z_candidate * tourney (struct z_candidate *, tsubst_flags_t);
+static struct z_candidate * tourney (struct z_candidate *, tsubst_flags_t,
+				     bool);
 static int equal_functions (tree, tree);
 static int joust (struct z_candidate *, struct z_candidate *, bool,
-		  tsubst_flags_t);
+		  tsubst_flags_t, bool);
 static int compare_ics (conversion *, conversion *);
 static void maybe_warn_class_memaccess (location_t, tree,
 					const vec<tree, va_gc> *);
@@ -167,7 +168,7 @@ static tree convert_like_with_context (conversion *, tree, tree, \
int,  static void op_error (const op_location_t &, enum tree_code, enum tree_code,
 		      tree, tree, tree, bool);
 static struct z_candidate *build_user_type_conversion_1 (tree, tree, int,
-							 tsubst_flags_t);
+							 tsubst_flags_t, bool);
 static void print_z_candidate (location_t, const char *, struct z_candidate *);
 static void print_z_candidates (location_t, struct z_candidate *);
 static tree build_this (tree);
@@ -1880,7 +1881,7 @@ 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.  */
       z_candidate *cand = build_user_type_conversion_1 (rto, expr, flags,
-							complain);
+							complain, c_cast_p);
       if (cand)
 	return cand->second_conv;
     }
@@ -2088,7 +2089,7 @@ implicit_conversion_1 (tree to, tree from, tree expr, bool \
c_cast_p,  && !CLASSTYPE_NON_AGGREGATE (complete_type (to)))
 	return build_aggr_conv (to, expr, flags, complain);
 
-      cand = build_user_type_conversion_1 (to, expr, flags, complain);
+      cand = build_user_type_conversion_1 (to, expr, flags, complain, c_cast_p);
       if (cand)
 	{
 	  if (BRACE_ENCLOSED_INITIALIZER_P (expr)
@@ -4122,7 +4123,7 @@ add_list_candidates (tree fns, tree first_arg,
 
 static struct z_candidate *
 build_user_type_conversion_1 (tree totype, tree expr, int flags,
-			      tsubst_flags_t complain)
+			      tsubst_flags_t complain, bool c_cast_p)
 {
   struct z_candidate *candidates, *cand;
   tree fromtype;
@@ -4281,7 +4282,7 @@ build_user_type_conversion_1 (tree totype, tree expr, int \
flags,  = implicit_conversion (totype,
 				   rettype,
 				   0,
-				   /*c_cast_p=*/false, convflags,
+				   c_cast_p, convflags,
 				   complain);
 
 	  /* If LOOKUP_NO_TEMP_BIND isn't set, then this is
@@ -4359,7 +4360,7 @@ build_user_type_conversion_1 (tree totype, tree expr, int \
flags,  return NULL;
     }
 
-  cand = tourney (candidates, complain);
+  cand = tourney (candidates, complain, c_cast_p);
   if (cand == NULL)
     {
       if (complain & tf_error)
@@ -4427,13 +4428,14 @@ build_user_type_conversion_1 (tree totype, tree expr, int \
flags,  
 tree
 build_user_type_conversion (tree totype, tree expr, int flags,
-			    tsubst_flags_t complain)
+			    tsubst_flags_t complain, bool c_cast_p)
 {
   struct z_candidate *cand;
   tree ret;
 
   auto_cond_timevar tv (TV_OVERLOAD);
-  cand = build_user_type_conversion_1 (totype, expr, flags, complain);
+  cand = build_user_type_conversion_1 (totype, expr, flags, complain,
+				       c_cast_p);
 
   if (cand)
     {
@@ -4715,7 +4717,7 @@ perform_overload_resolution (tree fn,
 
   *candidates = splice_viable (*candidates, false, any_viable_p);
   if (*any_viable_p)
-    cand = tourney (*candidates, complain);
+    cand = tourney (*candidates, complain, /*c_cast_p=*/false);
   else
     cand = NULL;
 
@@ -5086,7 +5088,7 @@ build_op_call (tree obj, vec<tree, va_gc> **args, \
tsubst_flags_t complain)  }
   else
     {
-      cand = tourney (candidates, complain);
+      cand = tourney (candidates, complain, /*c_cast_p=*/false);
       if (cand == 0)
 	{
           if (complain & tf_error)
@@ -5739,7 +5741,7 @@ build_conditional_expr (const op_location_t &loc,
 		      arg2_type, arg3_type);
 	  return error_mark_node;
 	}
-      cand = tourney (candidates, complain);
+      cand = tourney (candidates, complain, /*c_cast_p=*/false);
       if (!cand)
 	{
           if (complain & tf_error)
@@ -6658,7 +6660,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, \
int flags,  }
   else
     {
-      cand = tourney (candidates, complain);
+      cand = tourney (candidates, complain, /*c_cast_p=*/false);
       if (cand == 0)
 	{
 	  if (complain & tf_error)
@@ -6795,7 +6797,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, \
int flags,  {
 	      struct candidate_warning *w;
 	      for (w = cand->warnings; w; w = w->next)
-		joust (cand, w->loser, 1, complain);
+		joust (cand, w->loser, 1, complain, /*c_cast_p=*/false);
 	    }
 
 	  /* Check for comparison of different enum types.  */
@@ -7014,7 +7016,7 @@ build_op_subscript (const op_location_t &loc, tree obj,
     }
   else
     {
-      cand = tourney (candidates, complain);
+      cand = tourney (candidates, complain, /*c_cast_p=*/false);
       if (cand == 0)
 	{
 	  if (complain & tf_error)
@@ -7888,7 +7890,8 @@ convert_like_internal (conversion *convs, tree expr, tree fn, \
int argnum,  /* We chose the surrogate function from add_conv_candidate, now we
 	     actually need to build the conversion.  */
 	  cand = build_user_type_conversion_1 (totype, expr,
-					       LOOKUP_NO_CONVERSION, complain);
+					       LOOKUP_NO_CONVERSION, complain,
+					       c_cast_p);
 
 	tree convfn = cand->fn;
 
@@ -7993,7 +7996,8 @@ convert_like_internal (conversion *convs, tree expr, tree fn, \
int argnum,  /* Call build_user_type_conversion again for the error.  */
 	  int flags = (convs->need_temporary_p
 		       ? LOOKUP_IMPLICIT : LOOKUP_NORMAL);
-	  build_user_type_conversion (totype, convs->u.expr, flags, complain);
+	  build_user_type_conversion (totype, convs->u.expr, flags, complain,
+				      c_cast_p);
 	  gcc_assert (seen_error ());
 	  maybe_inform_about_fndecl_for_bogus_argument_init (fn, argnum);
 	}
@@ -9251,7 +9255,7 @@ build_over_call (struct z_candidate *cand, int flags, \
tsubst_flags_t complain)  {
       struct candidate_warning *w;
       for (w = cand->warnings; w; w = w->next)
-	joust (cand, w->loser, 1, complain);
+	joust (cand, w->loser, 1, complain, /*c_cast_p=*/false);
     }
 
   /* Core issue 2327: P0135 doesn't say how to handle the case where the
@@ -11034,7 +11038,7 @@ build_new_method_call (tree instance, tree fns, vec<tree, \
va_gc> **args,  }
   else
     {
-      cand = tourney (candidates, complain);
+      cand = tourney (candidates, complain, /*c_cast_p=*/false);
       if (cand == 0)
 	{
 	  char *pretty_name;
@@ -11953,19 +11957,31 @@ cand_parms_match (z_candidate *c1, z_candidate *c2)
 
 static int
 joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
-       tsubst_flags_t complain)
+       tsubst_flags_t complain, bool c_cast_p)
 {
   int winner = 0;
   int off1 = 0, off2 = 0;
   size_t i;
   size_t len;
 
+  /* If a (C-style) conversion can be interpreted in more than one of the ways
+     listed above, the interpretation that appears first in the list is used,
+     even if a cast resulting from that interpretation is ill-formed.  */
+  if (c_cast_p)
+    {
+      /* Since this is a user-defined conversion, it must be a static_cast
+	 optionally followed by a const_cast.  */
+      int qual1 = cand1->second_conv->kind == ck_qual;
+      int qual2 = cand2->second_conv->kind == ck_qual;
+      if (qual1 != qual2)
+	winner = qual1 ? 2 : 1;
+      /* Don't return yet, since we need to generate various warnings.  */
+    }
+
   /* Candidates that involve bad conversions are always worse than those
-     that don't.  */
-  if (cand1->viable > cand2->viable)
-    return 1;
-  if (cand1->viable < cand2->viable)
-    return -1;
+     that don't - except when performing a C-style cast.  */
+  if (!c_cast_p && cand1->viable != cand2->viable)
+    return cand1->viable > cand2->viable ? 1 : -1;
 
   /* If we have two pseudo-candidates for conversions to the same type,
      or two candidates for the same function, arbitrarily pick one.  */
@@ -12479,7 +12495,8 @@ tweak:
    algorithm.  */
 
 static struct z_candidate *
-tourney (struct z_candidate *candidates, tsubst_flags_t complain)
+tourney (struct z_candidate *candidates, tsubst_flags_t complain,
+	 bool c_cast_p)
 {
   struct z_candidate *champ = candidates, *challenger;
   int fate;
@@ -12490,7 +12507,7 @@ tourney (struct z_candidate *candidates, tsubst_flags_t \
complain)  
   for (challenger = champ->next; challenger; )
     {
-      fate = joust (champ, challenger, 0, complain);
+      fate = joust (champ, challenger, 0, complain, c_cast_p);
       if (fate == 1)
 	challenger = challenger->next;
       else
@@ -12520,7 +12537,7 @@ tourney (struct z_candidate *candidates, tsubst_flags_t \
complain)  && !(champ_compared_to_predecessor && challenger->next == champ);
        challenger = challenger->next)
     {
-      fate = joust (champ, challenger, 0, complain);
+      fate = joust (champ, challenger, 0, complain, c_cast_p);
       if (fate != 1)
 	return NULL;
     }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5fc9e5efdab..bc1317becb0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6487,7 +6487,7 @@ extern tree extract_call_expr			(tree);
 extern tree build_trivial_dtor_call		(tree, bool = false);
 extern bool ref_conv_binds_directly_p		(tree, tree);
 extern tree build_user_type_conversion		(tree, tree, int,
-						 tsubst_flags_t);
+						 tsubst_flags_t, bool = false);
 extern tree build_new_function_call		(tree, vec<tree, va_gc> **,
 						 tsubst_flags_t);
 extern tree build_operator_new_call		(tree, vec<tree, va_gc> **,
diff --git a/gcc/testsuite/g++.dg/conversion/cwg909.C \
b/gcc/testsuite/g++.dg/conversion/cwg909.C new file mode 100644
index 00000000000..c914aadc45c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/cwg909.C
@@ -0,0 +1,11 @@
+// Per CWG 909, the two casts below have the same validity and meaning, since:
+// "one possible interpretation of an old-style cast is as a static_cast followed by \
a const_cast." +
+struct S {
+  operator const int* ();
+};
+
+void f(S& s)  {
+  const_cast<int*>(static_cast<const int*>(s));  // #1
+  (int*) s;  // #2
+}
diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/misc17.C \
b/gcc/testsuite/g++.old-deja/g++.brendan/misc17.C index 4052664159e..c1163aba042 \
                100644
--- a/gcc/testsuite/g++.old-deja/g++.brendan/misc17.C
+++ b/gcc/testsuite/g++.old-deja/g++.brendan/misc17.C
@@ -32,5 +32,7 @@ FwtStdProgram::usage_if_not_complete()
 {
 	FwtStdProgram& thisp = *this;
 	thisp.form("%s: error, there were unrecognized options",
-		   (char *) FwtErrorManager::_program);// { dg-error "" } .*
+		   (char *) FwtErrorManager::_program); // OK, same as \
const_cast<char*>(static_cast<char const*>(...)) +	thisp.form("%s: error, there were \
unrecognized options", +		   reinterpret_cast<char *>(FwtErrorManager::_program)); // \
{ dg-error "" } .*  }
diff --git a/gcc/testsuite/g++.old-deja/g++.mike/p2855.C \
b/gcc/testsuite/g++.old-deja/g++.mike/p2855.C index 48a3fe42f45..39e25b014af 100644
--- a/gcc/testsuite/g++.old-deja/g++.mike/p2855.C
+++ b/gcc/testsuite/g++.old-deja/g++.mike/p2855.C
@@ -16,6 +16,7 @@ Ctest::operator const char *() const
 int main()
 {
   Ctest obj;
-  char* temp = (char *)obj;		// { dg-error "16:invalid cast" } 
+  char* temp = reinterpret_cast<char *>(obj); // { dg-error "16:invalid cast" }
   temp[0] = '\0';
+  char* temp2 = (char *)obj; // OK, const_cast<char *>(static_cast<char const \
*>(...))  }
-- 
2.30.2



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic