diff mbox

PR64959: SFINAE in UDLs

Message ID CAGqc0-o0xXBL8We01z+bn_+sT1Cs-vHh1axmJguDK0GHpCVdNw@mail.gmail.com
State New
Headers show

Commit Message

Andrea Azzarone Feb. 9, 2015, 1:27 p.m. UTC
Hi all,

atm enable_if cannot be used to select between overload of UDLs. E.g.
https://gcc.gnu.org/bugzilla/attachment.cgi?id=34684 will not compile.
This can be easily fixed making sure that lookup_literal_operator
returns all the possible candidates and not just the first match. I
made some more changes in parser.c to improve diagnostic in case of
failures. Four testcases added:
    * udlit-sfinae.C
    * udlit-sfinae.neg.C
    * udlit-char-template-sfinae.C
    * udlit-char-template-sfinae-neg.C

The first make check showed a failures in udlit-resolve.C. Actually
the failures was a false positive, because UDLs lookup is a normal
unqualified name lookup. I added two tests:
  * udlit-namespace-ambiguous.C
  * udlit-namespace-ambiguous-neg.C

At the end I also noticed a bug in string template literals. As per
N3599 the lookup should give precedence to operator""_t(const char*,
unsigned long). I updated the code in parser.C and added a test.

Please note that this is my first gcc patch :)

2015-2-9 Andrea Azzarone <azzaronea@gmail.com>
    PR c++/64959

    * gcc/cp/parser.c: Make sure lookup_literal_operator returns all
the possible candidates. Also improve the diagnostic messages.
    * gcc/testsuite/g++.dg/cpp0x/udlit-namespace-ambiguous.C: Add test
case to make sure gcc detects ambiguous UDL declarations.
    * gcc/testsuite/g++.dg/cpp0x/udlit-namespace-using-directive.C:
Add test case to make sure gcc correctly handles using directive for
UDLs.
    * gcc/testsuite/g++.dg/cpp0x/udlit-resolve.C: Remove a incorrect test case.
    * gcc/testsuite/g++.dg/cpp0x/udlit-sfinae.C: Add a test case to
make sure that enable_if can be used to select between overloads of
UDLs.
    * gcc/testsuite/g++.dg/cpp0x/udlit-sfinae-neg.C: Add a test case
to make sure gcc correctly detects substitution failures when all the
UDSL overloads are disabled by enable_if.
    * gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae-neg.C:
Like cpp0x/udlit-sfinae-neg.C but for string template literals.
    * gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C: Like
cpp0x/udlit-sfinae.C but for string template literals.
    * gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C:
Add a test to make sure that string template literals have a smaller
priority than standard literal operators.

Comments

Jason Merrill Feb. 10, 2015, 5:18 p.m. UTC | #1
On 02/09/2015 08:27 AM, Andrea Azzarone wrote:
> Please note that this is my first gcc patch

Thanks, looks good!  A couple of nits:

>     * gcc/cp/parser.c: Make sure lookup_literal_operator returns all
> the possible candidates. Also improve the diagnostic messages.

A ChangeLog entry should be per function, e.g.

	* parser.c (lookup_literal_operator): Return all candidates.
	(cp_parser_userdef_numeric_literal): Pass tf_warning_or_error.
	(cp_parser_userdef_string_literal): Likewise.  Prefer the
	non-template form.

> @@ -4044,31 +4061,15 @@ cp_parser_userdef_string_literal (tree l
>      {
>        tree tmpl_args = make_string_pack (value);
>        decl = lookup_template_function (decl, tmpl_args);
> -      result = finish_call_expr (decl, &args, false, true, tf_none);
> +      result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
>        if (result != error_mark_node)
> -	{
> -	  release_tree_vector (args);
> -	  return result;
> -	}
> +        {
> +          release_tree_vector (args);
> +          return result;
> +        }
>      }

Why not remove the test against error_mark_node here like you did in 
cp_parser_userdef_numeric_literal?

Jason
diff mbox

Patch

Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 220454)
+++ gcc/cp/parser.c	(working copy)
@@ -3828,7 +3828,7 @@  lookup_literal_operator (tree name, vec<
 		 work in presence of default arguments on the literal
 		 operator parameters.  */
 	      && parmtypes == void_list_node)
-	    return fn;
+	    return decl;
 	}
     }
 
@@ -3955,7 +3955,7 @@  cp_parser_userdef_numeric_literal (cp_pa
   decl = lookup_literal_operator (name, args);
   if (decl && decl != error_mark_node)
     {
-      result = finish_call_expr (decl, &args, false, true, tf_none);
+      result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
       if (result != error_mark_node)
 	{
 	  if (TREE_CODE (TREE_TYPE (value)) == INTEGER_TYPE && overflow > 0)
@@ -3986,7 +3986,7 @@  cp_parser_userdef_numeric_literal (cp_pa
   decl = lookup_literal_operator (name, args);
   if (decl && decl != error_mark_node)
     {
-      result = finish_call_expr (decl, &args, false, true, tf_none);
+      result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
       if (result != error_mark_node)
 	{
 	  release_tree_vector (args);
@@ -4004,13 +4004,12 @@  cp_parser_userdef_numeric_literal (cp_pa
     {
       tree tmpl_args = make_char_string_pack (num_string);
       decl = lookup_template_function (decl, tmpl_args);
-      result = finish_call_expr (decl, &args, false, true, tf_none);
-      if (result != error_mark_node)
-	{
-	  release_tree_vector (args);
-	  return result;
-	}
+      result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
+
+      release_tree_vector (args);
+      return result;
     }
+
   release_tree_vector (args);
 
   error ("unable to find numeric literal operator %qD", name);
@@ -4035,6 +4034,24 @@  cp_parser_userdef_string_literal (tree l
   tree decl, result;
   vec<tree, va_gc> *args;
 
+  /* Build up a call to the user-defined operator  */
+  /* Lookup the name we got back from the id-expression.  */
+  args = make_tree_vector ();
+  vec_safe_push (args, value);
+  vec_safe_push (args, build_int_cst (size_type_node, len));
+  decl = lookup_literal_operator (name, args);
+
+  if (decl && decl != error_mark_node)
+    {
+      result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
+      if (result != error_mark_node)
+        {
+          release_tree_vector (args);
+          return result;
+        }
+    }
+  release_tree_vector (args);
+
   /* Look for a template function with typename parameter CharT
      and parameter pack CharT...  Call the function with
      template parameter characters representing the string.  */
@@ -4044,31 +4061,15 @@  cp_parser_userdef_string_literal (tree l
     {
       tree tmpl_args = make_string_pack (value);
       decl = lookup_template_function (decl, tmpl_args);
-      result = finish_call_expr (decl, &args, false, true, tf_none);
+      result = finish_call_expr (decl, &args, false, true, tf_warning_or_error);
       if (result != error_mark_node)
-	{
-	  release_tree_vector (args);
-	  return result;
-	}
+        {
+          release_tree_vector (args);
+          return result;
+        }
     }
   release_tree_vector (args);
 
-  /* Build up a call to the user-defined operator  */
-  /* Lookup the name we got back from the id-expression.  */
-  args = make_tree_vector ();
-  vec_safe_push (args, value);
-  vec_safe_push (args, build_int_cst (size_type_node, len));
-  decl = lookup_name (name);
-  if (!decl || decl == error_mark_node)
-    {
-      error ("unable to find string literal operator %qD", name);
-      release_tree_vector (args);
-      return error_mark_node;
-    }
-  result = finish_call_expr (decl, &args, false, true, tf_none);
-  release_tree_vector (args);
-  if (result != error_mark_node)
-    return result;
 
   error ("unable to find string literal operator %qD with %qT, %qT arguments",
 	 name, TREE_TYPE (value), size_type_node);
Index: gcc/testsuite/g++.dg/cpp0x/udlit-namespace-ambiguous.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/udlit-namespace-ambiguous.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/udlit-namespace-ambiguous.C	(working copy)
@@ -0,0 +1,18 @@ 
+// { dg-do compile { target c++11 } }
+
+int operator""_t(long long unsigned); // { dg-message "note: candidate"}
+
+namespace foo {
+  int operator""_t(long long unsigned);  // { dg-message "note: candidate"}
+}
+
+using namespace foo;
+
+int main()
+{
+  10_t;
+}
+
+// { dg-error "call of overloaded|is ambiguous" 13}
+// { dg-error "unable to find numeric literal operator" 13}
+// { dg-message "note: use -std=gnu++11 or -fext-numeric-literals to enable more built-in suffixes" }
Index: gcc/testsuite/g++.dg/cpp0x/udlit-namespace-using-directive.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/udlit-namespace-using-directive.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/udlit-namespace-using-directive.C	(working copy)
@@ -0,0 +1,16 @@ 
+// { dg-do compile { target c++11 } }
+
+int operator""_t(long long unsigned) {
+    return 0;
+}
+
+namespace foo {
+  int operator""_t(long long unsigned) {
+    return 0;
+  }
+}
+
+int main() {
+  using foo::operator""_t;
+  10_t;
+}
Index: gcc/testsuite/g++.dg/cpp0x/udlit-resolve.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/udlit-resolve.C	(revision 220454)
+++ gcc/testsuite/g++.dg/cpp0x/udlit-resolve.C	(working copy)
@@ -17,15 +17,10 @@  int operator"" _foo(const char32_t*, std
 template<char...> int operator"" _foo2()          { return 20; }
 int operator"" _foo2(unsigned long long int)      { return 21; }
 
-namespace bar {
-int operator"" _foo(unsigned long long int)       { return 101; }
-}
-using namespace bar;
-
 int
 main()
 {
-  assert(123_foo == 101);
+  assert(123_foo == 1);
   assert(0.123_foo == 2);
   assert('c'_foo == 3);
   assert(L'c'_foo == 4);
Index: gcc/testsuite/g++.dg/cpp0x/udlit-sfinae-neg.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/udlit-sfinae-neg.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/udlit-sfinae-neg.C	(working copy)
@@ -0,0 +1,21 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-prune-output "note:" }
+
+template<bool, typename _Tp = void> struct enable_if { };
+template<typename _Tp> struct enable_if<true, _Tp> { typedef _Tp type; };
+
+template <char... c>
+constexpr typename enable_if<sizeof...(c) == 2, int>::type operator""_t() // { dg-error "no type named|in" }
+{
+  return 2;
+}
+
+template <char... c>
+constexpr typename enable_if<sizeof...(c) == 1, int>::type operator""_t() // { dg-error "no type named|in" } 
+{
+  return 1;
+}
+
+int a = 45_t;
+int b = 4_t;
+int c = 100000_t; // { dg-error "no matching function for call to" }
Index: gcc/testsuite/g++.dg/cpp0x/udlit-sfinae.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/udlit-sfinae.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/udlit-sfinae.C	(working copy)
@@ -0,0 +1,38 @@ 
+// { dg-do run { target c++11 } }
+
+#include <cassert>
+
+template<bool, typename _Tp = void> struct enable_if { };
+template<typename _Tp> struct enable_if<true, _Tp> { typedef _Tp type; };
+
+
+template <char... c>
+constexpr typename enable_if<sizeof...(c) == 2, int>::type operator""_t()
+{
+  return 2;
+}
+
+template <char... c>
+constexpr typename enable_if<sizeof...(c) == 1, int>::type operator""_t()
+{
+  return 1;
+}
+
+template <char... c>
+constexpr typename enable_if<sizeof...(c) >= 3, int>::type operator""_t()
+{
+  return 100;
+}
+
+int operator""_t(long double)
+{
+  return 200;
+}
+
+int main()
+{
+  assert(45_t == 2);
+  assert(4_t == 1);
+  assert(100000_t == 100);
+  assert(200.0_t == 200);
+}
Index: gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae-neg.C
===================================================================
--- gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae-neg.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae-neg.C	(working copy)
@@ -0,0 +1,21 @@ 
+// { dg-do compile { target c++14 } }
+// { dg-prune-output "note:" }
+
+template<bool, typename _Tp = void>struct enable_if {};
+template<typename _Tp> struct enable_if<true, _Tp> { typedef _Tp type; };
+
+
+template<typename CharT, CharT... String>
+typename enable_if<sizeof...(String) == 1, int>::type operator"" _script() { // { dg-error "no type named|in" }
+  return 1;
+}
+
+template<typename CharT, CharT... String>
+typename enable_if<sizeof...(String) == 2, int>::type operator"" _script() {  // { dg-error "no type named|in" }
+  return 2;
+}
+
+int a = "1"_script;
+int b = "22"_script;
+int c = "333"_script; // { dg-error "no matching function for call to"}
+                      // { dg-error "unable to find string literal operator" 20}
Index: gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C
===================================================================
--- gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C	(working copy)
@@ -0,0 +1,29 @@ 
+// { dg-do run { target c++14 } }
+
+#include <cassert>
+
+template<bool, typename _Tp = void>struct enable_if {};
+template<typename _Tp> struct enable_if<true, _Tp> { typedef _Tp type; };
+
+
+template<typename CharT, CharT... String>
+typename enable_if<sizeof...(String) == 6, int>::type operator"" _script() { 
+  return 5;
+}
+
+template<typename CharT, CharT... String>
+typename enable_if<sizeof...(String) == 3, int>::type operator"" _script() { 
+  return 3;
+}
+
+template<typename CharT, CharT... String>
+typename enable_if<sizeof...(String) != 3 && sizeof...(String) != 6, int>::type operator"" _script() { 
+  return 1;
+}
+
+int main()
+{
+  assert("hello!"_script == 5);
+  assert(u8"hi!"_script == 3);
+  assert("hey!"_script == 1);
+}
Index: gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C
===================================================================
--- gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C	(working copy)
@@ -0,0 +1,17 @@ 
+// { dg-do run { target c++14 } }
+
+#include <cassert>
+
+template<typename CharT, CharT... String>
+int operator"" _script() { 
+  return 1;
+}
+
+int operator"" _script(const char*, unsigned long) {
+  return 2;
+}
+
+int main ()
+{
+  assert("123"_script == 2);
+}