diff mbox series

C++ PATCH for c++/79228, complex literal suffixes

Message ID CADzB+2nOhF+ZEbid8AMXbxjHP8=Rkfp59hnREOFrKAv=NeQCcw@mail.gmail.com
State New
Headers show
Series C++ PATCH for c++/79228, complex literal suffixes | expand

Commit Message

Jason Merrill Dec. 1, 2017, 8:16 p.m. UTC
79228 points out that C++14 defines complex literal suffixes that
conflict with the GNU suffixes.  In this patch I take the approach
that in C++14 and up, if <complex> has been included we assume that
the user wants the C++14 suffixes and give a hard error if they aren't
found; otherwise we assume that the user wants the GNU suffixes and
give a pedwarn if -Wpedantic.

While looking at this, I noticed that David's #include suggestion code
wasn't suggesting <complex> for references to std::complex, so the
second patch addresses that.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit e39b7d506d236ce7ef9f64d1bcf0b384bb2d8038
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Dec 1 07:45:03 2017 -0500

            PR c++/79228 - extensions hide C++14 complex literal operators
    
    libcpp/
            * expr.c (interpret_float_suffix): Ignore 'i' in C++14 and up.
            (interpret_int_suffix): Likewise.
    gcc/cp/
            * parser.c (cp_parser_userdef_numeric_literal): Be helpful about
            'i' in C++14 and up.
commit 59576ea299c27f6b3f29eb5e6171949a72720dbe
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Dec 1 13:22:40 2017 -0500

            Give #include hints for <complex>.
    
            * name-lookup.c (get_std_name_hint): Add <complex>.
            * parser.c (cp_parser_diagnose_invalid_type_name): Call
            suggest_alternative_in_explicit_scope.
            (cp_parser_namespace_name): Likewise.

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 9f65c4d7992..62ea564cedd 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5414,6 +5414,9 @@ get_std_name_hint (const char *name)
   static const std_name_hint hints[] = {
     /* <array>.  */
     {"array", "<array>"}, // C++11
+    /* <complex>.  */
+    {"complex", "<complex>"},
+    {"complex_literals", "<complex>"},
     /* <deque>.  */
     {"deque", "<deque>"},
     /* <forward_list>.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6e4c24362c6..891f742eb21 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -3365,6 +3365,9 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
 		      id, parser->scope);
 	  if (DECL_P (decl))
 	    inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl);
+	  else if (decl == error_mark_node)
+	    suggest_alternative_in_explicit_scope (location, id,
+						   parser->scope);
 	}
       else if (CLASS_TYPE_P (parser->scope)
 	       && constructor_name_p (id, parser->scope))
@@ -18408,7 +18411,13 @@ cp_parser_namespace_name (cp_parser* parser)
       || TREE_CODE (namespace_decl) != NAMESPACE_DECL)
     {
       if (!cp_parser_uncommitted_to_tentative_parse_p (parser))
-	error_at (token->location, "%qD is not a namespace-name", identifier);
+	{
+	  error_at (token->location, "%qD is not a namespace-name", identifier);
+	  if (namespace_decl == error_mark_node
+	      && parser->scope && TREE_CODE (parser->scope) == NAMESPACE_DECL)
+	    suggest_alternative_in_explicit_scope (token->location, identifier,
+						   parser->scope);
+	}
       cp_parser_error (parser, "expected namespace-name");
       namespace_decl = error_mark_node;
     }
diff --git a/gcc/testsuite/g++.dg/lookup/missing-std-include-4.C b/gcc/testsuite/g++.dg/lookup/missing-std-include-4.C
new file mode 100644
index 00000000000..abf4e96bd3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/missing-std-include-4.C
@@ -0,0 +1,2 @@
+std::complex<int> c;		// { dg-error "" }
+// { dg-message "#include <complex>" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/lookup/missing-std-include-5.C b/gcc/testsuite/g++.dg/lookup/missing-std-include-5.C
new file mode 100644
index 00000000000..fe880a6263b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/missing-std-include-5.C
@@ -0,0 +1,2 @@
+using namespace std::complex_literals; // { dg-error "" }
+// { dg-message "#include <complex>" "" { target *-*-* } .-1 }

Comments

Jakub Jelinek Dec. 5, 2017, 12:33 p.m. UTC | #1
On Fri, Dec 01, 2017 at 03:16:23PM -0500, Jason Merrill wrote:
> commit e39b7d506d236ce7ef9f64d1bcf0b384bb2d8038
> Author: Jason Merrill <jason@redhat.com>
> Date:   Fri Dec 1 07:45:03 2017 -0500
> 
>             PR c++/79228 - extensions hide C++14 complex literal operators
>     
>     libcpp/
>             * expr.c (interpret_float_suffix): Ignore 'i' in C++14 and up.
>             (interpret_int_suffix): Likewise.
>     gcc/cp/
>             * parser.c (cp_parser_userdef_numeric_literal): Be helpful about
>             'i' in C++14 and up.
> 
> +      /* In C++14 and up these suffixes are in the standard library, so treat
> +	 them as user-defined literals.  */
> +      if (CPP_OPTION (pfile, cplusplus)
> +	  && CPP_OPTION (pfile, lang) > CLK_CXX11
> +	  && (!memcmp (orig_s, "i", orig_len)
> +	      || !memcmp (orig_s, "if", orig_len)
> +	      || !memcmp (orig_s, "il", orig_len)))

This doesn't seem right, it will invoke UB if orig_len > 2 by potentially
accessing bytes beyond 'i' and '\0' in "i" (and for orig_len > 3 also after
"if" or "il").
If you only want to return 0 if orig_len bytes starting at orig_s are 'i'
or 'i' 'f' or 'i' 'l', then I'd write instead as in the patch below.
Or if memcmp is more readable, at least check orig_len first.

> +      /* In C++14 and up these suffixes are in the standard library, so treat
> +	 them as user-defined literals.  */
> +      if (CPP_OPTION (pfile, cplusplus)
> +	  && CPP_OPTION (pfile, lang) > CLK_CXX11
> +	  && (!memcmp (s, "i", orig_len)
> +	      || !memcmp (s, "if", orig_len)
> +	      || !memcmp (s, "il", orig_len)))

Similarly.  Additionally, "if" can't happen here, because we don't allow 'f'
among suffixes.

So, ok for trunk if it passes testing, or some other form thereof?

2017-12-05  Jakub Jelinek  <jakub@redhat.com>

	PR c++/79228
	* expr.c (interpret_float_suffix): Avoid memcmp.
	(interpret_int_suffix): Likewise.  Don't check for if.

--- libcpp/expr.c.jj	2017-12-01 22:13:24.000000000 +0100
+++ libcpp/expr.c	2017-12-05 13:26:57.019683785 +0100
@@ -280,9 +280,10 @@ interpret_float_suffix (cpp_reader *pfil
 	 them as user-defined literals.  */
       if (CPP_OPTION (pfile, cplusplus)
 	  && CPP_OPTION (pfile, lang) > CLK_CXX11
-	  && (!memcmp (orig_s, "i", orig_len)
-	      || !memcmp (orig_s, "if", orig_len)
-	      || !memcmp (orig_s, "il", orig_len)))
+	  && orig_s[0] == 'i'
+	  && (orig_len == 1
+	      || (orig_len == 2
+		  && (orig_s[1] == 'f' || orig_s[1] == 'l'))))
 	return 0;
     }
 
@@ -345,9 +346,8 @@ interpret_int_suffix (cpp_reader *pfile,
 	 them as user-defined literals.  */
       if (CPP_OPTION (pfile, cplusplus)
 	  && CPP_OPTION (pfile, lang) > CLK_CXX11
-	  && (!memcmp (s, "i", orig_len)
-	      || !memcmp (s, "if", orig_len)
-	      || !memcmp (s, "il", orig_len)))
+	  && s[0] == 'i'
+	  && (orig_len == 1 || (orig_len == 2 && s[1] == 'l')))
 	return 0;
     }
 


	Jakub
Jason Merrill Dec. 5, 2017, 2:24 p.m. UTC | #2
OK, thanks.

On Tue, Dec 5, 2017 at 7:33 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Dec 01, 2017 at 03:16:23PM -0500, Jason Merrill wrote:
>> commit e39b7d506d236ce7ef9f64d1bcf0b384bb2d8038
>> Author: Jason Merrill <jason@redhat.com>
>> Date:   Fri Dec 1 07:45:03 2017 -0500
>>
>>             PR c++/79228 - extensions hide C++14 complex literal operators
>>
>>     libcpp/
>>             * expr.c (interpret_float_suffix): Ignore 'i' in C++14 and up.
>>             (interpret_int_suffix): Likewise.
>>     gcc/cp/
>>             * parser.c (cp_parser_userdef_numeric_literal): Be helpful about
>>             'i' in C++14 and up.
>>
>> +      /* In C++14 and up these suffixes are in the standard library, so treat
>> +      them as user-defined literals.  */
>> +      if (CPP_OPTION (pfile, cplusplus)
>> +       && CPP_OPTION (pfile, lang) > CLK_CXX11
>> +       && (!memcmp (orig_s, "i", orig_len)
>> +           || !memcmp (orig_s, "if", orig_len)
>> +           || !memcmp (orig_s, "il", orig_len)))
>
> This doesn't seem right, it will invoke UB if orig_len > 2 by potentially
> accessing bytes beyond 'i' and '\0' in "i" (and for orig_len > 3 also after
> "if" or "il").
> If you only want to return 0 if orig_len bytes starting at orig_s are 'i'
> or 'i' 'f' or 'i' 'l', then I'd write instead as in the patch below.
> Or if memcmp is more readable, at least check orig_len first.
>
>> +      /* In C++14 and up these suffixes are in the standard library, so treat
>> +      them as user-defined literals.  */
>> +      if (CPP_OPTION (pfile, cplusplus)
>> +       && CPP_OPTION (pfile, lang) > CLK_CXX11
>> +       && (!memcmp (s, "i", orig_len)
>> +           || !memcmp (s, "if", orig_len)
>> +           || !memcmp (s, "il", orig_len)))
>
> Similarly.  Additionally, "if" can't happen here, because we don't allow 'f'
> among suffixes.
>
> So, ok for trunk if it passes testing, or some other form thereof?
>
> 2017-12-05  Jakub Jelinek  <jakub@redhat.com>
>
>         PR c++/79228
>         * expr.c (interpret_float_suffix): Avoid memcmp.
>         (interpret_int_suffix): Likewise.  Don't check for if.
>
> --- libcpp/expr.c.jj    2017-12-01 22:13:24.000000000 +0100
> +++ libcpp/expr.c       2017-12-05 13:26:57.019683785 +0100
> @@ -280,9 +280,10 @@ interpret_float_suffix (cpp_reader *pfil
>          them as user-defined literals.  */
>        if (CPP_OPTION (pfile, cplusplus)
>           && CPP_OPTION (pfile, lang) > CLK_CXX11
> -         && (!memcmp (orig_s, "i", orig_len)
> -             || !memcmp (orig_s, "if", orig_len)
> -             || !memcmp (orig_s, "il", orig_len)))
> +         && orig_s[0] == 'i'
> +         && (orig_len == 1
> +             || (orig_len == 2
> +                 && (orig_s[1] == 'f' || orig_s[1] == 'l'))))
>         return 0;
>      }
>
> @@ -345,9 +346,8 @@ interpret_int_suffix (cpp_reader *pfile,
>          them as user-defined literals.  */
>        if (CPP_OPTION (pfile, cplusplus)
>           && CPP_OPTION (pfile, lang) > CLK_CXX11
> -         && (!memcmp (s, "i", orig_len)
> -             || !memcmp (s, "if", orig_len)
> -             || !memcmp (s, "il", orig_len)))
> +         && s[0] == 'i'
> +         && (orig_len == 1 || (orig_len == 2 && s[1] == 'l')))
>         return 0;
>      }
>
>
>
>         Jakub
diff mbox series

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index b469d1c1760..6e4c24362c6 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -4397,11 +4397,75 @@  cp_parser_userdef_numeric_literal (cp_parser *parser)
 
   release_tree_vector (args);
 
-  error ("unable to find numeric literal operator %qD", name);
-  if (!cpp_get_options (parse_in)->ext_numeric_literals)
-    inform (token->location, "use -std=gnu++11 or -fext-numeric-literals "
+  /* In C++14 the standard library defines complex number suffixes that
+     conflict with GNU extensions.  Prefer them if <complex> is #included.  */
+  bool ext = cpp_get_options (parse_in)->ext_numeric_literals;
+  bool i14 = (cxx_dialect > cxx11
+	      && (id_equal (suffix_id, "i")
+		  || id_equal (suffix_id, "if")
+		  || id_equal (suffix_id, "il")));
+  diagnostic_t kind = DK_ERROR;
+  int opt = 0;
+
+  if (i14 && ext)
+    {
+      tree cxlit = lookup_qualified_name (std_node,
+					  get_identifier ("complex_literals"),
+					  0, false, false);
+      if (cxlit == error_mark_node)
+	{
+	  /* No <complex>, so pedwarn and use GNU semantics.  */
+	  kind = DK_PEDWARN;
+	  opt = OPT_Wpedantic;
+	}
+    }
+
+  bool complained
+    = emit_diagnostic (kind, input_location, opt,
+		       "unable to find numeric literal operator %qD", name);
+
+  if (!complained)
+    /* Don't inform either.  */;
+  else if (i14)
+    {
+      inform (token->location, "add %<using namespace std::complex_literals%> "
+	      "(from <complex>) to enable the C++14 user-defined literal "
+	      "suffixes");
+      if (ext)
+	inform (token->location, "or use %<j%> instead of %<i%> for the "
+		"GNU built-in suffix");
+    }
+  else if (!ext)
+    inform (token->location, "use -fext-numeric-literals "
 	    "to enable more built-in suffixes");
-  return error_mark_node;
+
+  if (kind == DK_ERROR)
+    value = error_mark_node;
+  else
+    {
+      /* Use the built-in semantics.  */
+      tree type;
+      if (id_equal (suffix_id, "i"))
+	{
+	  if (TREE_CODE (value) == INTEGER_CST)
+	    type = integer_type_node;
+	  else
+	    type = double_type_node;
+	}
+      else if (id_equal (suffix_id, "if"))
+	type = float_type_node;
+      else /* if (id_equal (suffix_id, "il")) */
+	type = long_double_type_node;
+
+      value = build_complex (build_complex_type (type),
+			     fold_convert (type, integer_zero_node),
+			     fold_convert (type, value));
+    }
+
+  if (cp_parser_uncommitted_to_tentative_parse_p (parser))
+    /* Avoid repeated diagnostics.  */
+    token->u.value = value;
+  return value;
 }
 
 /* Parse a user-defined string constant.  Returns a call to a user-defined
diff --git a/gcc/testsuite/g++.dg/cpp0x/gnu_fext-numeric-literals.C b/gcc/testsuite/g++.dg/cpp0x/gnu_fext-numeric-literals.C
index 6a8398b896a..ac2db287f3a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/gnu_fext-numeric-literals.C
+++ b/gcc/testsuite/g++.dg/cpp0x/gnu_fext-numeric-literals.C
@@ -4,7 +4,7 @@ 
 //  Integer imaginary...
 
 constexpr unsigned long long
-operator"" i(unsigned long long n) // { dg-warning "shadowed by implementation" }
+operator"" i(unsigned long long n) // { dg-warning "shadowed by implementation" "" { target c++11_only } }
 { return 4 * n + 0; }
 
 constexpr unsigned long long
@@ -22,7 +22,7 @@  operator"" J(unsigned long long n) // { dg-warning "shadowed by implementation"
 //  Floating-point imaginary...
 
 constexpr long double
-operator"" i(long double n) // { dg-warning "shadowed by implementation" }
+operator"" i(long double n) // { dg-warning "shadowed by implementation" "" { target c++11_only } }
 { return 4.0L * n + 0.0L; }
 
 constexpr long double
diff --git a/gcc/testsuite/g++.dg/cpp0x/std_fext-numeric-literals.C b/gcc/testsuite/g++.dg/cpp0x/std_fext-numeric-literals.C
index 7caaa7cee8d..ff1e7b6d966 100644
--- a/gcc/testsuite/g++.dg/cpp0x/std_fext-numeric-literals.C
+++ b/gcc/testsuite/g++.dg/cpp0x/std_fext-numeric-literals.C
@@ -4,7 +4,7 @@ 
 //  Integer imaginary...
 
 constexpr unsigned long long
-operator"" i(unsigned long long n) // { dg-warning "shadowed by implementation" }
+operator"" i(unsigned long long n) // { dg-warning "shadowed by implementation" "" { target c++11_only } }
 { return 4 * n + 0; }
 
 constexpr unsigned long long
@@ -22,7 +22,7 @@  operator"" J(unsigned long long n) // { dg-warning "shadowed by implementation"
 //  Floating-point imaginary...
 
 constexpr long double
-operator"" i(long double n) // { dg-warning "shadowed by implementation" }
+operator"" i(long double n) // { dg-warning "shadowed by implementation" "" { target c++11_only } }
 { return 4.0L * n + 0.0L; }
 
 constexpr long double
diff --git a/gcc/testsuite/g++.dg/cpp1y/complex_literals1.C b/gcc/testsuite/g++.dg/cpp1y/complex_literals1.C
new file mode 100644
index 00000000000..5ae2370c3ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/complex_literals1.C
@@ -0,0 +1,10 @@ 
+// PR c++/79228
+// { dg-do compile { target c++14 } }
+
+#include <complex>
+
+int main()
+{
+  using namespace std::complex_literals;
+  auto a = std::abs(0.0i);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/complex_literals1a.C b/gcc/testsuite/g++.dg/cpp1y/complex_literals1a.C
new file mode 100644
index 00000000000..9b61f3aa534
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/complex_literals1a.C
@@ -0,0 +1,11 @@ 
+// PR c++/79228
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+#include <complex>
+
+int main()
+{
+  auto a = std::abs(0.0i);	// { dg-error "literal operator" }
+  // { dg-message "complex_literals" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/complex_literals2.C b/gcc/testsuite/g++.dg/cpp1y/complex_literals2.C
new file mode 100644
index 00000000000..6dd9947dab8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/complex_literals2.C
@@ -0,0 +1,25 @@ 
+// PR c++/79228
+// { dg-do compile { target c++14 } }
+// { dg-options "-Wpedantic" }
+
+template <class,class> struct same;
+template <class T> struct same<T,T> { };
+
+int main()
+{
+  same<decltype(0i),__complex int>{}; // { dg-warning "literal operator" }
+  // { dg-message "complex_literals" "" { target *-*-* } .-1 }
+  // { dg-message "built-in" "" { target *-*-* } .-2 }
+
+  same<decltype(0.0i),__complex double>{}; // { dg-warning "literal operator" }
+  // { dg-message "complex_literals" "" { target *-*-* } .-1 }
+  // { dg-message "built-in" "" { target *-*-* } .-2 }
+
+  same<decltype(0.0if),__complex float>{}; // { dg-warning "literal operator" }
+  // { dg-message "complex_literals" "" { target *-*-* } .-1 }
+  // { dg-message "built-in" "" { target *-*-* } .-2 }
+
+  same<decltype(0.0il),__complex long double>{}; // { dg-warning "literal operator" }
+  // { dg-message "complex_literals" "" { target *-*-* } .-1 }
+  // { dg-message "built-in" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/complex_literals2a.C b/gcc/testsuite/g++.dg/cpp1y/complex_literals2a.C
new file mode 100644
index 00000000000..9ee9f3fd24d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/complex_literals2a.C
@@ -0,0 +1,14 @@ 
+// PR c++/79228
+// { dg-do compile { target c++14 } }
+// { dg-options "" }
+
+template <class,class> struct same;
+template <class T> struct same<T,T> { };
+
+int main()
+{
+  same<decltype(0i),__complex int>{};
+  same<decltype(0.0i),__complex double>{};
+  same<decltype(0.0if),__complex float>{};
+  same<decltype(0.0il),__complex long double>{};
+}
diff --git a/libcpp/expr.c b/libcpp/expr.c
index 04675d8faee..fe9f6b0188c 100644
--- a/libcpp/expr.c
+++ b/libcpp/expr.c
@@ -90,6 +90,8 @@  static cpp_num parse_has_include (cpp_reader *, enum include_type);
 static unsigned int
 interpret_float_suffix (cpp_reader *pfile, const uchar *s, size_t len)
 {
+  size_t orig_len = len;
+  const uchar *orig_s = s;
   size_t flags;
   size_t f, d, l, w, q, i, fn, fnx, fn_bits;
 
@@ -269,8 +271,20 @@  interpret_float_suffix (cpp_reader *pfile, const uchar *s, size_t len)
   if (fn && fn_bits == 96)
     return 0;
 
-  if (i && !CPP_OPTION (pfile, ext_numeric_literals))
-    return 0;
+  if (i)
+    {
+      if (!CPP_OPTION (pfile, ext_numeric_literals))
+	return 0;
+
+      /* In C++14 and up these suffixes are in the standard library, so treat
+	 them as user-defined literals.  */
+      if (CPP_OPTION (pfile, cplusplus)
+	  && CPP_OPTION (pfile, lang) > CLK_CXX11
+	  && (!memcmp (orig_s, "i", orig_len)
+	      || !memcmp (orig_s, "if", orig_len)
+	      || !memcmp (orig_s, "il", orig_len)))
+	return 0;
+    }
 
   if ((w || q) && !CPP_OPTION (pfile, ext_numeric_literals))
     return 0;
@@ -299,6 +313,7 @@  cpp_interpret_float_suffix (cpp_reader *pfile, const char *s, size_t len)
 static unsigned int
 interpret_int_suffix (cpp_reader *pfile, const uchar *s, size_t len)
 {
+  size_t orig_len = len;
   size_t u, l, i;
 
   u = l = i = 0;
@@ -321,8 +336,20 @@  interpret_int_suffix (cpp_reader *pfile, const uchar *s, size_t len)
   if (l > 2 || u > 1 || i > 1)
     return 0;
 
-  if (i && !CPP_OPTION (pfile, ext_numeric_literals))
-    return 0;
+  if (i)
+    {
+      if (!CPP_OPTION (pfile, ext_numeric_literals))
+	return 0;
+
+      /* In C++14 and up these suffixes are in the standard library, so treat
+	 them as user-defined literals.  */
+      if (CPP_OPTION (pfile, cplusplus)
+	  && CPP_OPTION (pfile, lang) > CLK_CXX11
+	  && (!memcmp (s, "i", orig_len)
+	      || !memcmp (s, "if", orig_len)
+	      || !memcmp (s, "il", orig_len)))
+	return 0;
+    }
 
   return ((i ? CPP_N_IMAGINARY : 0)
 	  | (u ? CPP_N_UNSIGNED : 0)