From 6498dea6b231eb72d70ffeb250f716f62fcc18da Mon Sep 17 00:00:00 2001 From: Paolo Carlini Date: Wed, 18 Jul 2018 10:27:12 +0000 Subject: [PATCH] re PR c++/59480 (Missing error diagnostic: friend declaration specifying a default argument must be a definition) /cp 2018-07-18 Paolo Carlini * class.c (note_name_declared_in_class): Prefer permerror + inform to a pair of permerrors; use DECL_SOURCE_LOCATION. /testsuite 2018-07-18 Paolo Carlini * g++.dg/ext/uow-3.C: Adjust. * g++.dg/ext/uow-4.C: Likewise. * g++.dg/lookup/name-clash11.C: Likewise. * g++.dg/lookup/name-clash7.C: Likewise. * g++.dg/lookup/redecl1.C: Likewise. * g++.dg/warn/changes-meaning.C: Likewise. * g++.old-deja/g++.jason/scoping8.C: Likewise. * g++.old-deja/g++.law/nest1.C: Likewise. /cp 2019-07-18 Paolo Carlini PR c++/59480, DR 136 * decl.c (check_no_redeclaration_friend_default_args): New. (duplicate_decls): Use the latter; also check that a friend declaration specifying default arguments is a definition. /testsuite 2019-07-18 Paolo Carlini PR c++/59480, DR 136 * g++.dg/other/friend8.C: New. * g++.dg/other/friend9.C: Likewise. * g++.dg/other/friend10.C: Likewise. * g++.dg/other/friend11.C: Likewise. * g++.dg/other/friend12.C: Likewise. * g++.dg/parse/defarg4.C: Compile with -fpermissive -w. * g++.dg/parse/defarg8.C: Likewise. From-SVN: r262851 --- gcc/cp/ChangeLog | 12 ++++ gcc/cp/class.c | 10 +-- gcc/cp/decl.c | 67 ++++++++++++++++++- gcc/testsuite/ChangeLog | 22 ++++++ gcc/testsuite/g++.dg/ext/uow-3.C | 4 +- gcc/testsuite/g++.dg/ext/uow-4.C | 4 +- gcc/testsuite/g++.dg/lookup/name-clash11.C | 20 +++--- gcc/testsuite/g++.dg/lookup/name-clash7.C | 4 +- gcc/testsuite/g++.dg/lookup/redecl1.C | 4 +- gcc/testsuite/g++.dg/other/friend10.C | 9 +++ gcc/testsuite/g++.dg/other/friend11.C | 8 +++ gcc/testsuite/g++.dg/other/friend12.C | 11 +++ gcc/testsuite/g++.dg/other/friend8.C | 6 ++ gcc/testsuite/g++.dg/other/friend9.C | 9 +++ gcc/testsuite/g++.dg/parse/defarg4.C | 2 +- gcc/testsuite/g++.dg/parse/defarg8.C | 2 + gcc/testsuite/g++.dg/warn/changes-meaning.C | 4 +- .../g++.old-deja/g++.jason/scoping8.C | 4 +- gcc/testsuite/g++.old-deja/g++.law/nest1.C | 4 +- 19 files changed, 174 insertions(+), 32 deletions(-) create mode 100644 gcc/testsuite/g++.dg/other/friend10.C create mode 100644 gcc/testsuite/g++.dg/other/friend11.C create mode 100644 gcc/testsuite/g++.dg/other/friend12.C create mode 100644 gcc/testsuite/g++.dg/other/friend8.C create mode 100644 gcc/testsuite/g++.dg/other/friend9.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 7161565280a..e9574f3e903 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,15 @@ +2019-07-18 Paolo Carlini + + PR c++/59480, DR 136 + * decl.c (check_no_redeclaration_friend_default_args): New. + (duplicate_decls): Use the latter; also check that a friend + declaration specifying default arguments is a definition. + +2018-07-18 Paolo Carlini + + * class.c (note_name_declared_in_class): Prefer permerror + inform + to a pair of permerrors; use DECL_SOURCE_LOCATION. + 2018-07-18 Richard Biener PR debug/86523 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 3d155a55500..4300c5cacf3 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -8285,10 +8285,12 @@ note_name_declared_in_class (tree name, tree decl) A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. */ - permerror (input_location, "declaration of %q#D", decl); - permerror (location_of ((tree) n->value), - "changes meaning of %qD from %q#D", - OVL_NAME (decl), (tree) n->value); + if (permerror (DECL_SOURCE_LOCATION (decl), + "declaration of %q#D changes meaning of %qD", + decl, OVL_NAME (decl))) + inform (location_of ((tree) n->value), + "%qD declared here as %q#D", + OVL_NAME (decl), (tree) n->value); } } diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 3c1e2ef3698..5239ffd5a07 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1280,6 +1280,39 @@ check_redeclaration_no_default_args (tree decl) } } +/* NEWDECL is a redeclaration of a function or function template OLDDECL. + If either the declaration or the redeclaration is a friend declaration + and specifies default arguments issue a diagnostic. Note: this is to + enforce C++17 11.3.6/4: "If a friend declaration specifies a default + argument expression, that declaration... shall be the only declaration + of the function or function template in the translation unit." */ + +static void +check_no_redeclaration_friend_default_args (tree olddecl, tree newdecl) +{ + bool olddecl_friend_p = DECL_FRIEND_P (STRIP_TEMPLATE (olddecl)); + bool newdecl_friend_p = DECL_FRIEND_P (STRIP_TEMPLATE (newdecl)); + + if (!olddecl_friend_p && !newdecl_friend_p) + return; + + tree t1 = FUNCTION_FIRST_USER_PARMTYPE (olddecl); + tree t2 = FUNCTION_FIRST_USER_PARMTYPE (newdecl); + + for (; t1 && t1 != void_list_node; + t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) + if ((olddecl_friend_p && TREE_PURPOSE (t1)) + || (newdecl_friend_p && TREE_PURPOSE (t2))) + { + if (permerror (DECL_SOURCE_LOCATION (newdecl), + "friend declaration of %q#D specifies default " + "arguments and isn't the only declaration", newdecl)) + inform (DECL_SOURCE_LOCATION (olddecl), + "previous declaration of %q#D", olddecl); + return; + } +} + /* Merge tree bits that correspond to attributes noreturn, nothrow, const, malloc, and pure from NEWDECL with those of OLDDECL. */ @@ -1876,6 +1909,12 @@ next_arg:; olddecl); } } + + /* C++17 11.3.6/4: "If a friend declaration specifies a default + argument expression, that declaration... shall be the only + declaration of the function or function template in the + translation unit." */ + check_no_redeclaration_friend_default_args (olddecl, newdecl); } } } @@ -2008,11 +2047,18 @@ next_arg:; if (DECL_FUNCTION_TEMPLATE_P (newdecl)) { - /* Per C++11 8.3.6/4, default arguments cannot be added in later - declarations of a function template. */ if (DECL_SOURCE_LOCATION (newdecl) != DECL_SOURCE_LOCATION (olddecl)) - check_redeclaration_no_default_args (newdecl); + { + /* Per C++11 8.3.6/4, default arguments cannot be added in + later declarations of a function template. */ + check_redeclaration_no_default_args (newdecl); + /* C++17 11.3.6/4: "If a friend declaration specifies a default + argument expression, that declaration... shall be the only + declaration of the function or function template in the + translation unit." */ + check_no_redeclaration_friend_default_args (olddecl, newdecl); + } check_default_args (newdecl); @@ -8763,6 +8809,21 @@ grokfndecl (tree ctype, } } + /* C++17 11.3.6/4: "If a friend declaration specifies a default argument + expression, that declaration shall be a definition..." */ + if (friendp && !funcdef_flag) + { + for (tree t = FUNCTION_FIRST_USER_PARMTYPE (decl); + t && t != void_list_node; t = TREE_CHAIN (t)) + if (TREE_PURPOSE (t)) + { + permerror (DECL_SOURCE_LOCATION (decl), + "friend declaration of %qD specifies default " + "arguments and isn't a definition", decl); + break; + } + } + /* If this decl has namespace scope, set that up. */ if (in_namespace) set_decl_namespace (decl, in_namespace, friendp); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7fc2a42fbce..b9b89224fc2 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,25 @@ +2019-07-18 Paolo Carlini + + PR c++/59480, DR 136 + * g++.dg/other/friend8.C: New. + * g++.dg/other/friend9.C: Likewise. + * g++.dg/other/friend10.C: Likewise. + * g++.dg/other/friend11.C: Likewise. + * g++.dg/other/friend12.C: Likewise. + * g++.dg/parse/defarg4.C: Compile with -fpermissive -w. + * g++.dg/parse/defarg8.C: Likewise. + +2018-07-18 Paolo Carlini + + * g++.dg/ext/uow-3.C: Adjust. + * g++.dg/ext/uow-4.C: Likewise. + * g++.dg/lookup/name-clash11.C: Likewise. + * g++.dg/lookup/name-clash7.C: Likewise. + * g++.dg/lookup/redecl1.C: Likewise. + * g++.dg/warn/changes-meaning.C: Likewise. + * g++.old-deja/g++.jason/scoping8.C: Likewise. + * g++.old-deja/g++.law/nest1.C: Likewise. + 2018-07-18 Richard Biener PR debug/86523 diff --git a/gcc/testsuite/g++.dg/ext/uow-3.C b/gcc/testsuite/g++.dg/ext/uow-3.C index a2c2240214b..9378c62564a 100644 --- a/gcc/testsuite/g++.dg/ext/uow-3.C +++ b/gcc/testsuite/g++.dg/ext/uow-3.C @@ -1,8 +1,8 @@ /* { dg-do compile } */ /* { dg-options "-Wall" } */ -typedef int UOW; /* { dg-error "" } */ +typedef int UOW; /* { dg-message "declared here" } */ struct ABC { - UOW UOW; /* { dg-error "" } */ + UOW UOW; /* { dg-error "changes meaning" } */ }; diff --git a/gcc/testsuite/g++.dg/ext/uow-4.C b/gcc/testsuite/g++.dg/ext/uow-4.C index 21ed04a88ea..73a3a6fa11c 100644 --- a/gcc/testsuite/g++.dg/ext/uow-4.C +++ b/gcc/testsuite/g++.dg/ext/uow-4.C @@ -3,9 +3,9 @@ extern "C" { -typedef int UOW; /* { dg-error "" } */ +typedef int UOW; /* { dg-message "declared here" } */ struct ABC { - UOW UOW; /* { dg-error "" } */ + UOW UOW; /* { dg-error "changes meaning" } */ }; } diff --git a/gcc/testsuite/g++.dg/lookup/name-clash11.C b/gcc/testsuite/g++.dg/lookup/name-clash11.C index 28ce4c937c5..bc63645e8d3 100644 --- a/gcc/testsuite/g++.dg/lookup/name-clash11.C +++ b/gcc/testsuite/g++.dg/lookup/name-clash11.C @@ -13,11 +13,11 @@ void test_bitset () { - int x; // { dg-warning "changes meaning" } + int x; // { dg-message "declared here" } { struct S { - int x: sizeof x; // { dg-warning "declaration" } + int x: sizeof x; // { dg-warning "changes meaning" } }; } } @@ -25,11 +25,11 @@ void test_bitset () void test_enum () { // Also exercise (not covered by c++/69023): - int y; // { dg-warning "changes meaning" } + int y; // { dg-message "declared here" } { struct S { enum E { - y = sizeof y // { dg-warning "declaration" } + y = sizeof y // { dg-warning "9:declaration of .y. changes meaning" } }; // Verify the enumerator has the correct value. @@ -40,7 +40,7 @@ void test_enum () void test_alignas () { - enum { A = 16 }; // { dg-warning "changes meaning" } + enum { A = 16 }; // { dg-message "declared here" } { struct S { #if __cplusplus >= 201103L @@ -48,7 +48,7 @@ void test_alignas () #else __attribute__ ((aligned (A))) #endif - int A; // { dg-warning "declaration" } + int A; // { dg-warning "changes meaning" } // Verify the member has the correct alignment. void test () { ASSERT (__alignof__ (this->A) == 16); } @@ -58,10 +58,10 @@ void test_alignas () void test_array () { - enum { A = 16 }; // { dg-warning "changes meaning" } + enum { A = 16 }; // { dg-message "declared here" } { struct S { - int A [A]; // { dg-warning "declaration" } + int A [A]; // { dg-warning "changes meaning" } // Verify the member has the correct alignment. void test () { ASSERT (sizeof (this->A) == 16 * sizeof (int)); } @@ -71,10 +71,10 @@ void test_array () void test_vector () { - enum { A = 16 }; // { dg-warning "changes meaning" } + enum { A = 16 }; // { dg-message "declared here" } { struct S { - int A __attribute__ ((vector_size (A))); // { dg-warning "declaration" } + int A __attribute__ ((vector_size (A))); // { dg-warning "changes meaning" } // Verify the member has the correct size. void test () { ASSERT (sizeof (this->A) == 16); } diff --git a/gcc/testsuite/g++.dg/lookup/name-clash7.C b/gcc/testsuite/g++.dg/lookup/name-clash7.C index 5c0690aa850..cc27181744d 100644 --- a/gcc/testsuite/g++.dg/lookup/name-clash7.C +++ b/gcc/testsuite/g++.dg/lookup/name-clash7.C @@ -1,11 +1,11 @@ // PR c++/28513 -class foo { // { dg-error "changes meaning" } +class foo { // { dg-message "declared here" } public: typedef int bar; }; class baz { public: - foo::bar foo; // { dg-error "declaration" } + foo::bar foo; // { dg-error "changes meaning" } }; diff --git a/gcc/testsuite/g++.dg/lookup/redecl1.C b/gcc/testsuite/g++.dg/lookup/redecl1.C index 436316ca070..b105be25e75 100644 --- a/gcc/testsuite/g++.dg/lookup/redecl1.C +++ b/gcc/testsuite/g++.dg/lookup/redecl1.C @@ -1,7 +1,7 @@ // PR c++/14668 -class A {}; // { dg-error "" } +class A {}; // { dg-message "declared here" } class B { - static A *A; // { dg-error "" } + static A *A; // { dg-error "changes meaning" } }; A *B::A = 0; diff --git a/gcc/testsuite/g++.dg/other/friend10.C b/gcc/testsuite/g++.dg/other/friend10.C new file mode 100644 index 00000000000..c162395210b --- /dev/null +++ b/gcc/testsuite/g++.dg/other/friend10.C @@ -0,0 +1,9 @@ +// PR c++/59480 + +class test { + friend int foo(bool = true) { return 1; } // { dg-message "14:previous" } + template friend int bar(bool = true) { return 1; } // { dg-message "33:previous" } +}; + +int foo(bool); // { dg-error "5:friend declaration" } +template int bar(bool); // { dg-error "24:friend declaration" } diff --git a/gcc/testsuite/g++.dg/other/friend11.C b/gcc/testsuite/g++.dg/other/friend11.C new file mode 100644 index 00000000000..b82b39ddbe3 --- /dev/null +++ b/gcc/testsuite/g++.dg/other/friend11.C @@ -0,0 +1,8 @@ +// PR c++/59480 + +class test { + friend int foo(bool = true) { return 1; } // { dg-message "14:previous" } + friend int foo(bool); // { dg-error "14:friend declaration" } + template friend int bar(bool = true) { return 1; } // { dg-message "33:previous" } + template friend int bar(bool); // { dg-error "33:friend declaration" } +}; diff --git a/gcc/testsuite/g++.dg/other/friend12.C b/gcc/testsuite/g++.dg/other/friend12.C new file mode 100644 index 00000000000..b78ce4b078c --- /dev/null +++ b/gcc/testsuite/g++.dg/other/friend12.C @@ -0,0 +1,11 @@ +// PR c++/59480 + +template +class test { + friend int foo(bool = true) { return 1; } // { dg-message "14:previous" } + friend int foo(bool); // { dg-error "14:friend declaration" } + template friend int bar(bool = true) { return 1; } // { dg-message "33:previous" } + template friend int bar(bool); // { dg-error "33:friend declaration" } +}; + +template class test; diff --git a/gcc/testsuite/g++.dg/other/friend8.C b/gcc/testsuite/g++.dg/other/friend8.C new file mode 100644 index 00000000000..6b5df88d6a4 --- /dev/null +++ b/gcc/testsuite/g++.dg/other/friend8.C @@ -0,0 +1,6 @@ +// PR c++/59480 + +class test { + friend int foo(bool = true); // { dg-error "14:friend declaration" } + template friend int bar(bool = true); // { dg-error "33:friend declaration" } +}; diff --git a/gcc/testsuite/g++.dg/other/friend9.C b/gcc/testsuite/g++.dg/other/friend9.C new file mode 100644 index 00000000000..16b4f57335d --- /dev/null +++ b/gcc/testsuite/g++.dg/other/friend9.C @@ -0,0 +1,9 @@ +// PR c++/59480 + +template +class test { + friend int foo(bool = true); // { dg-error "14:friend declaration" } + template friend int bar(bool = true); // { dg-error "33:friend declaration" } +}; + +template class test; diff --git a/gcc/testsuite/g++.dg/parse/defarg4.C b/gcc/testsuite/g++.dg/parse/defarg4.C index 151f6c5f668..ad8a1ed6c65 100644 --- a/gcc/testsuite/g++.dg/parse/defarg4.C +++ b/gcc/testsuite/g++.dg/parse/defarg4.C @@ -1,4 +1,4 @@ -// { dg-do compile } +// { dg-options "-fpermissive -w" } // Copyright (C) 2003 Free Software Foundation, Inc. // Contributed by Nathan Sidwell 3 Jul 2003 diff --git a/gcc/testsuite/g++.dg/parse/defarg8.C b/gcc/testsuite/g++.dg/parse/defarg8.C index 1f1f078aa16..33100069ead 100644 --- a/gcc/testsuite/g++.dg/parse/defarg8.C +++ b/gcc/testsuite/g++.dg/parse/defarg8.C @@ -1,3 +1,5 @@ +// { dg-options "-fpermissive -w" } + struct A { static void g(int); }; diff --git a/gcc/testsuite/g++.dg/warn/changes-meaning.C b/gcc/testsuite/g++.dg/warn/changes-meaning.C index fdbddf82091..0b9fc4cbe8e 100644 --- a/gcc/testsuite/g++.dg/warn/changes-meaning.C +++ b/gcc/testsuite/g++.dg/warn/changes-meaning.C @@ -1,11 +1,11 @@ /* { dg-do compile } */ /* { dg-options "-fpermissive" } */ -template class auto_ptr {}; /* { dg-warning "changes meaning" } */ +template class auto_ptr {}; /* { dg-message "declared here" } */ template class counted_ptr { public: - auto_ptr<_Tp> auto_ptr(); /* { dg-warning "" } */ + auto_ptr<_Tp> auto_ptr(); /* { dg-warning "17:declaration of .auto_ptr\\<_Tp\\>" } */ }; diff --git a/gcc/testsuite/g++.old-deja/g++.jason/scoping8.C b/gcc/testsuite/g++.old-deja/g++.jason/scoping8.C index bb31735f228..fe3b3364601 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/scoping8.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/scoping8.C @@ -1,8 +1,8 @@ // { dg-do assemble } // Bug: g++ allows two different meanings of a name in the same scope. -typedef int foo; // { dg-error "" } +typedef int foo; // { dg-message "declared here" } struct A { A (foo); - int foo (); // { dg-error "" } foo already used in scope + int foo (); // { dg-error "changes meaning" } }; diff --git a/gcc/testsuite/g++.old-deja/g++.law/nest1.C b/gcc/testsuite/g++.old-deja/g++.law/nest1.C index 7b2cae55b4b..68ad6ae987a 100644 --- a/gcc/testsuite/g++.old-deja/g++.law/nest1.C +++ b/gcc/testsuite/g++.old-deja/g++.law/nest1.C @@ -6,10 +6,10 @@ // Subject: Local type names bug in g++ 2.3.3 // Message-ID: <1992Dec30.203807.17504@murdoch.acc.Virginia.EDU> -typedef char* T; // { dg-error "" } previous declaration +typedef char* T; // { dg-message "declared here" } struct Y { T a; - typedef long T; // error. See ARM p189-191 for details// { dg-error "" } + typedef long T; // { dg-error "changes meaning" } T b; }; -- 2.30.2