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 <ja...@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.

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)
commit 59576ea299c27f6b3f29eb5e6171949a72720dbe
Author: Jason Merrill <ja...@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 }

Reply via email to