C++ PATCH for c++/92032 - DR 1601: Promotion of enum with fixed underlying type
diff mbox series

Message ID 20191008221409.GA2949@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/92032 - DR 1601: Promotion of enum with fixed underlying type
Related show

Commit Message

Marek Polacek Oct. 8, 2019, 10:14 p.m. UTC
I've been messing with compare_ics recently and noticed that we don't
implement CWG 1601, which should be fairly easy.

The motivating example is 

  enum E : char { e };
  void f(char);
  void f(int);
  void g() {
    f(e);
  }

where the call to f was ambiguous but we should choose f(char).

Currently we give f(int) cr_promotion in standard_conversion, while
f(char) remains cr_std, which is worse than cr_promotion.  So I thought
I'd give it cr_promotion also and then add a tiebreaker to compare_ics.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-10-08  Marek Polacek  <polacek@redhat.com>

	PR c++/92032 - DR 1601: Promotion of enum with fixed underlying	type.
	* call.c (standard_conversion): When converting an enumeration with
	a fixed underlying type to the underlying type, give it the cr_promotion
	rank.
	(compare_ics): Implement a tiebreaker as per CWG 1601.

	* g++.dg/cpp0x/scoped_enum10.C: New test.
	* g++.dg/cpp0x/scoped_enum11.C: New test.

Comments

Jason Merrill Oct. 9, 2019, 5:28 p.m. UTC | #1
OK.

On 10/8/19 6:14 PM, Marek Polacek wrote:
> I've been messing with compare_ics recently and noticed that we don't
> implement CWG 1601, which should be fairly easy.
> 
> The motivating example is
> 
>    enum E : char { e };
>    void f(char);
>    void f(int);
>    void g() {
>      f(e);
>    }
> 
> where the call to f was ambiguous but we should choose f(char).
> 
> Currently we give f(int) cr_promotion in standard_conversion, while
> f(char) remains cr_std, which is worse than cr_promotion.  So I thought
> I'd give it cr_promotion also and then add a tiebreaker to compare_ics.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-10-08  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/92032 - DR 1601: Promotion of enum with fixed underlying	type.
> 	* call.c (standard_conversion): When converting an enumeration with
> 	a fixed underlying type to the underlying type, give it the cr_promotion
> 	rank.
> 	(compare_ics): Implement a tiebreaker as per CWG 1601.
> 
> 	* g++.dg/cpp0x/scoped_enum10.C: New test.
> 	* g++.dg/cpp0x/scoped_enum11.C: New test.
> 
> diff --git gcc/cp/call.c gcc/cp/call.c
> index 6c9acac4614..10172855d4d 100644
> --- gcc/cp/call.c
> +++ gcc/cp/call.c
> @@ -1484,8 +1484,18 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
>   
>         conv = build_conv (ck_std, to, conv);
>   
> -      /* Give this a better rank if it's a promotion.  */
> -      if (same_type_p (to, type_promotes_to (from))
> +      tree underlying_type = NULL_TREE;
> +      if (TREE_CODE (from) == ENUMERAL_TYPE
> +	  && ENUM_FIXED_UNDERLYING_TYPE_P (from))
> +	underlying_type = ENUM_UNDERLYING_TYPE (from);
> +
> +      /* Give this a better rank if it's a promotion.
> +
> +	 To handle CWG 1601, also bump the rank if we are converting
> +	 an enumeration with a fixed underlying type to the underlying
> +	 type.  */
> +      if ((same_type_p (to, type_promotes_to (from))
> +	   || (underlying_type && same_type_p (to, underlying_type)))
>   	  && next_conversion (conv)->rank <= cr_promotion)
>   	conv->rank = cr_promotion;
>       }
> @@ -10506,6 +10516,31 @@ compare_ics (conversion *ics1, conversion *ics2)
>   	}
>       }
>   
> +  /* [over.ics.rank]
> +
> +     Per CWG 1601:
> +     -- A conversion that promotes an enumeration whose underlying type
> +     is fixed to its underlying type is better than one that promotes to
> +     the promoted underlying type, if the two are different.  */
> +  if (ics1->rank == cr_promotion
> +      && ics2->rank == cr_promotion
> +      && UNSCOPED_ENUM_P (from_type1)
> +      && ENUM_FIXED_UNDERLYING_TYPE_P (from_type1)
> +      && same_type_p (from_type1, from_type2))
> +    {
> +      tree utype = ENUM_UNDERLYING_TYPE (from_type1);
> +      tree prom = type_promotes_to (from_type1);
> +      if (!same_type_p (utype, prom))
> +	{
> +	  if (same_type_p (to_type1, utype)
> +	      && same_type_p (to_type2, prom))
> +	    return 1;
> +	  else if (same_type_p (to_type2, utype)
> +		   && same_type_p (to_type1, prom))
> +	    return -1;
> +	}
> +    }
> +
>     /* Neither conversion sequence is better than the other.  */
>     return 0;
>   }
> diff --git gcc/testsuite/g++.dg/cpp0x/scoped_enum10.C gcc/testsuite/g++.dg/cpp0x/scoped_enum10.C
> new file mode 100644
> index 00000000000..b588581cd3e
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/scoped_enum10.C
> @@ -0,0 +1,37 @@
> +// PR c++/92032 - DR 1601: Promotion of enumeration with fixed underlying type.
> +// { dg-do compile { target c++11 } }
> +
> +enum E : char { e };
> +enum F : int { f };
> +enum G : long { g };
> +enum H : unsigned { h };
> +
> +int f1(char);
> +void f1(int);
> +
> +void f2(int);
> +int f2(char);
> +
> +int f3(int);
> +void f3(short);
> +
> +int f4(long);
> +void f4(int);
> +
> +void f5(unsigned);
> +int f5(int);
> +
> +int f6(unsigned);
> +void f6(int);
> +
> +void
> +test ()
> +{
> +  int r = 0;
> +  r += f1 (e);
> +  r += f2 (e);
> +  r += f3 (f);
> +  r += f4 (g);
> +  r += f5 (f);
> +  r += f6 (h);
> +}
> diff --git gcc/testsuite/g++.dg/cpp0x/scoped_enum11.C gcc/testsuite/g++.dg/cpp0x/scoped_enum11.C
> new file mode 100644
> index 00000000000..e6dcfbac9d8
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/scoped_enum11.C
> @@ -0,0 +1,35 @@
> +// PR c++/92032 - DR 1601: Promotion of enumeration with fixed underlying type.
> +// { dg-do compile { target c++11 } }
> +
> +enum E1 : long { e1 };
> +enum E2 : short { e2 };
> +
> +int f1(short);
> +void f1(int);
> +
> +void f2(int);
> +int f2(short);
> +
> +void f3(int);
> +int f3(long);
> +
> +int f4(short);
> +void f4(long);
> +
> +int f5(int);
> +void f5(long);
> +
> +int f6(unsigned int); // { dg-message "candidate" }
> +void f6(long); // { dg-message "candidate" }
> +
> +void
> +fn ()
> +{
> +  int r = 0;
> +  r += f1 (e2);
> +  r += f2 (e2);
> +  r += f3 (e1);
> +  r += f4 (e2);
> +  r += f5 (e2);
> +  r += f6 (e2); // { dg-error "ambiguous" }
> +}
>

Patch
diff mbox series

diff --git gcc/cp/call.c gcc/cp/call.c
index 6c9acac4614..10172855d4d 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -1484,8 +1484,18 @@  standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
 
       conv = build_conv (ck_std, to, conv);
 
-      /* Give this a better rank if it's a promotion.  */
-      if (same_type_p (to, type_promotes_to (from))
+      tree underlying_type = NULL_TREE;
+      if (TREE_CODE (from) == ENUMERAL_TYPE
+	  && ENUM_FIXED_UNDERLYING_TYPE_P (from))
+	underlying_type = ENUM_UNDERLYING_TYPE (from);
+
+      /* Give this a better rank if it's a promotion.
+
+	 To handle CWG 1601, also bump the rank if we are converting
+	 an enumeration with a fixed underlying type to the underlying
+	 type.  */
+      if ((same_type_p (to, type_promotes_to (from))
+	   || (underlying_type && same_type_p (to, underlying_type)))
 	  && next_conversion (conv)->rank <= cr_promotion)
 	conv->rank = cr_promotion;
     }
@@ -10506,6 +10516,31 @@  compare_ics (conversion *ics1, conversion *ics2)
 	}
     }
 
+  /* [over.ics.rank]
+
+     Per CWG 1601:
+     -- A conversion that promotes an enumeration whose underlying type
+     is fixed to its underlying type is better than one that promotes to
+     the promoted underlying type, if the two are different.  */
+  if (ics1->rank == cr_promotion
+      && ics2->rank == cr_promotion
+      && UNSCOPED_ENUM_P (from_type1)
+      && ENUM_FIXED_UNDERLYING_TYPE_P (from_type1)
+      && same_type_p (from_type1, from_type2))
+    {
+      tree utype = ENUM_UNDERLYING_TYPE (from_type1);
+      tree prom = type_promotes_to (from_type1);
+      if (!same_type_p (utype, prom))
+	{
+	  if (same_type_p (to_type1, utype)
+	      && same_type_p (to_type2, prom))
+	    return 1;
+	  else if (same_type_p (to_type2, utype)
+		   && same_type_p (to_type1, prom))
+	    return -1;
+	}
+    }
+
   /* Neither conversion sequence is better than the other.  */
   return 0;
 }
diff --git gcc/testsuite/g++.dg/cpp0x/scoped_enum10.C gcc/testsuite/g++.dg/cpp0x/scoped_enum10.C
new file mode 100644
index 00000000000..b588581cd3e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/scoped_enum10.C
@@ -0,0 +1,37 @@ 
+// PR c++/92032 - DR 1601: Promotion of enumeration with fixed underlying type.
+// { dg-do compile { target c++11 } }
+
+enum E : char { e };
+enum F : int { f };
+enum G : long { g };
+enum H : unsigned { h };
+
+int f1(char);
+void f1(int);
+
+void f2(int);
+int f2(char);
+
+int f3(int);
+void f3(short);
+
+int f4(long);
+void f4(int);
+
+void f5(unsigned);
+int f5(int);
+
+int f6(unsigned);
+void f6(int);
+
+void
+test ()
+{
+  int r = 0;
+  r += f1 (e);
+  r += f2 (e);
+  r += f3 (f);
+  r += f4 (g);
+  r += f5 (f);
+  r += f6 (h);
+}
diff --git gcc/testsuite/g++.dg/cpp0x/scoped_enum11.C gcc/testsuite/g++.dg/cpp0x/scoped_enum11.C
new file mode 100644
index 00000000000..e6dcfbac9d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/scoped_enum11.C
@@ -0,0 +1,35 @@ 
+// PR c++/92032 - DR 1601: Promotion of enumeration with fixed underlying type.
+// { dg-do compile { target c++11 } }
+
+enum E1 : long { e1 };
+enum E2 : short { e2 };
+
+int f1(short);
+void f1(int);
+
+void f2(int);
+int f2(short);
+
+void f3(int);
+int f3(long);
+
+int f4(short);
+void f4(long);
+
+int f5(int);
+void f5(long);
+
+int f6(unsigned int); // { dg-message "candidate" }
+void f6(long); // { dg-message "candidate" }
+
+void
+fn ()
+{
+  int r = 0;
+  r += f1 (e2);
+  r += f2 (e2);
+  r += f3 (e1);
+  r += f4 (e2);
+  r += f5 (e2);
+  r += f6 (e2); // { dg-error "ambiguous" }
+}