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 }