From 7873fcd66afba8cb976f4a8d438c39af1dcd592e Mon Sep 17 00:00:00 2001 From: mmitchel Date: Mon, 24 Apr 2006 03:50:31 +0000 Subject: [PATCH] PR c++/26912 * cp-tree.h (build_this_parm): Declare. (grok_method_quals): Remove. (build_memfn_type): Declare. (build_artificial_parm): Declare. (do_friend): Remove quals parameter. * decl.c (build_this_parm): New function. (grokfndecl): Use it. Do not pass quals to grokclassfn. (grokdeclarator): Rename quals to memfn_quals. Avoid allocating unnecessary TYPE_DECLs. Correct qualification of member function types. Tidy. * method.c (implicitly_declare_fn): Use build_this_parm. * friend.c (do_friend): Remove quals parameter. * decl2.c (grok_method_quals): Remove. (build_memfn_type): New function. (build_artificial_parm): Give it external linkage. (grokclassfn): Remove quals parameter. Do not build "this" PARM_DECL here. PR c++/26912 * g++.dg/template/friend41.C: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@113213 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/cp/ChangeLog | 19 +++ gcc/cp/cp-tree.h | 9 +- gcc/cp/decl.c | 192 ++++++++++++++++--------------- gcc/cp/decl2.c | 55 +++------ gcc/cp/friend.c | 9 +- gcc/cp/method.c | 11 +- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/g++.dg/template/friend41.C | 11 ++ 8 files changed, 164 insertions(+), 147 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/friend41.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a0a44a54075..0d24702efa8 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,24 @@ 2006-04-23 Mark Mitchell + PR c++/26912 + * cp-tree.h (build_this_parm): Declare. + (grok_method_quals): Remove. + (build_memfn_type): Declare. + (build_artificial_parm): Declare. + (do_friend): Remove quals parameter. + * decl.c (build_this_parm): New function. + (grokfndecl): Use it. Do not pass quals to grokclassfn. + (grokdeclarator): Rename quals to memfn_quals. Avoid allocating + unnecessary TYPE_DECLs. Correct qualification of member function + types. Tidy. + * method.c (implicitly_declare_fn): Use build_this_parm. + * friend.c (do_friend): Remove quals parameter. + * decl2.c (grok_method_quals): Remove. + (build_memfn_type): New function. + (build_artificial_parm): Give it external linkage. + (grokclassfn): Remove quals parameter. Do not build "this" + PARM_DECL here. + PR c++/26534 * cp-tree.h (is_bitfield_expr_with_lowered_type): New function. * typeck.c (is_bitfield_expr_with_lowered_type): New function. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 34cccc0a911..1e3c1b7807d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3842,6 +3842,7 @@ extern int cp_complete_array_type (tree *, tree, bool); extern tree build_ptrmemfunc_type (tree); extern tree build_ptrmem_type (tree, tree); /* the grokdeclarator prototype is in decl.h */ +extern tree build_this_parm (tree, cp_cv_quals); extern int copy_fn_p (tree); extern tree get_scope_of_declarator (const cp_declarator *); extern void grok_special_member_properties (tree); @@ -3899,12 +3900,11 @@ extern bool have_extern_spec; /* in decl2.c */ extern bool check_java_method (tree); -extern cp_cv_quals grok_method_quals (tree, tree, cp_cv_quals); +extern tree build_memfn_type (tree, tree, cp_cv_quals); extern void maybe_retrofit_in_chrg (tree); extern void maybe_make_one_only (tree); extern void grokclassfn (tree, tree, - enum overload_flags, - cp_cv_quals); + enum overload_flags); extern tree grok_array_decl (tree, tree); extern tree delete_sanity (tree, tree, bool, int); extern tree check_classfn (tree, tree, tree); @@ -3934,6 +3934,7 @@ extern tree cxx_callgraph_analyze_expr (tree *, int *, tree); extern void mark_needed (tree); extern bool decl_needed_p (tree); extern void note_vague_linkage_fn (tree); +extern tree build_artificial_parm (tree, tree); /* in error.c */ extern void init_error (void); @@ -3966,7 +3967,7 @@ extern tree cplus_expand_constant (tree); extern int is_friend (tree, tree); extern void make_friend_class (tree, tree, bool); extern void add_friend (tree, tree, bool); -extern tree do_friend (tree, tree, tree, tree, enum overload_flags, cp_cv_quals, bool); +extern tree do_friend (tree, tree, tree, tree, enum overload_flags, bool); /* in init.c */ extern tree expand_member_init (tree); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index d979fd3521a..c7967c7709b 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5808,6 +5808,28 @@ check_class_member_definition_namespace (tree decl) decl, DECL_CONTEXT (decl)); } +/* Build a PARM_DECL for the "this" parameter. TYPE is the + METHOD_TYPE for a non-static member function; QUALS are the + cv-qualifiers that apply to the function. */ + +tree +build_this_parm (tree type, cp_cv_quals quals) +{ + tree this_type; + tree qual_type; + tree parm; + cp_cv_quals this_quals; + + this_type = TREE_VALUE (TYPE_ARG_TYPES (type)); + /* The `this' parameter is implicitly `const'; it cannot be + assigned to. */ + this_quals = (quals & TYPE_QUAL_RESTRICT) | TYPE_QUAL_CONST; + qual_type = cp_build_qualified_type (this_type, this_quals); + parm = build_artificial_parm (this_identifier, qual_type); + cp_apply_type_quals_to_decl (this_quals, parm); + return parm; +} + /* CTYPE is class type, or null if non-class. TYPE is type this FUNCTION_DECL should have, either FUNCTION_TYPE or METHOD_TYPE. @@ -5854,6 +5876,13 @@ grokfndecl (tree ctype, type = build_exception_variant (type, raises); decl = build_lang_decl (FUNCTION_DECL, declarator, type); + if (TREE_CODE (type) == METHOD_TYPE) + { + tree parm; + parm = build_this_parm (type, quals); + TREE_CHAIN (parm) = parms; + parms = parm; + } DECL_ARGUMENTS (decl) = parms; /* Propagate volatile out from type to decl. */ if (TYPE_VOLATILE (type)) @@ -6057,7 +6086,7 @@ grokfndecl (tree ctype, if (sfk == sfk_constructor) DECL_CONSTRUCTOR_P (decl) = 1; - grokclassfn (ctype, decl, flags, quals); + grokclassfn (ctype, decl, flags); } decl = check_explicit_specialization (orig_declarator, decl, @@ -6765,7 +6794,6 @@ grokdeclarator (const cp_declarator *declarator, { tree type = NULL_TREE; int longlong = 0; - int type_quals; int virtualp, explicitp, friendp, inlinep, staticp; int explicit_int = 0; int explicit_char = 0; @@ -6792,7 +6820,11 @@ grokdeclarator (const cp_declarator *declarator, tree dname = NULL_TREE; tree ctor_return_type = NULL_TREE; enum overload_flags flags = NO_SPECIAL; - cp_cv_quals quals = TYPE_UNQUALIFIED; + /* cv-qualifiers that apply to the declarator, for a declaration of + a member function. */ + cp_cv_quals memfn_quals = TYPE_UNQUALIFIED; + /* cv-qualifiers that apply to the type specified by the DECLSPECS. */ + int type_quals; tree raises = NULL_TREE; int template_count = 0; tree returned_attrs = NULL_TREE; @@ -7451,7 +7483,7 @@ grokdeclarator (const cp_declarator *declarator, } /* Pick up type qualifiers which should be applied to `this'. */ - quals = declarator->u.function.qualifiers; + memfn_quals = declarator->u.function.qualifiers; /* Pick up the exception specifications. */ raises = declarator->u.function.exception_specification; @@ -7473,53 +7505,44 @@ grokdeclarator (const cp_declarator *declarator, is the same as the class name, and we are defining a function, then it is a constructor/destructor, and therefore returns a void type. */ - - if (flags == DTOR_FLAG) + + /* ISO C++ 12.4/2. A destructor may not be declared + const or volatile. A destructor may not be + static. + + ISO C++ 12.1. A constructor may not be declared + const or volatile. A constructor may not be + virtual. A constructor may not be static. */ + if (staticp == 2) + error ((flags == DTOR_FLAG) + ? "destructor cannot be static member function" + : "constructor cannot be static member function"); + if (memfn_quals) { - /* ISO C++ 12.4/2. A destructor may not be - declared const or volatile. A destructor may - not be static. */ - if (staticp == 2) - error ("destructor cannot be static member function"); - if (quals) - { - error ("destructors may not be cv-qualified"); - quals = TYPE_UNQUALIFIED; - } - if (decl_context == FIELD) - { - if (! member_function_or_else (ctype, - current_class_type, - flags)) - return void_type_node; - } + error ((flags == DTOR_FLAG) + ? "destructors may not be cv-qualified" + : "constructors may not be cv-qualified"); + memfn_quals = TYPE_UNQUALIFIED; } - else /* It's a constructor. */ + + if (decl_context == FIELD + && !member_function_or_else (ctype, + current_class_type, + flags)) + return void_type_node; + + if (flags != DTOR_FLAG) { + /* It's a constructor. */ if (explicitp == 1) explicitp = 2; - /* ISO C++ 12.1. A constructor may not be - declared const or volatile. A constructor may - not be virtual. A constructor may not be - static. */ - if (staticp == 2) - error ("constructor cannot be static member function"); if (virtualp) { pedwarn ("constructors cannot be declared virtual"); virtualp = 0; } - if (quals) - { - error ("constructors may not be cv-qualified"); - quals = TYPE_UNQUALIFIED; - } if (decl_context == FIELD) { - if (! member_function_or_else (ctype, - current_class_type, - flags)) - return void_type_node; TYPE_HAS_CONSTRUCTOR (ctype) = 1; if (sfk != sfk_constructor) return NULL_TREE; @@ -7560,7 +7583,6 @@ grokdeclarator (const cp_declarator *declarator, } type = build_function_type (type, arg_types); - type = cp_build_qualified_type (type, quals); } break; @@ -7590,22 +7612,13 @@ grokdeclarator (const cp_declarator *declarator, type_quals = TYPE_UNQUALIFIED; if (declarator->kind == cdk_ptrmem - && (TREE_CODE (type) == FUNCTION_TYPE - || (quals && TREE_CODE (type) == METHOD_TYPE))) + && (TREE_CODE (type) == FUNCTION_TYPE || memfn_quals)) { - tree dummy; - - /* If the type is a FUNCTION_TYPE, pick up the - qualifiers from that function type. No other - qualifiers may be supplied. */ - if (TREE_CODE (type) == FUNCTION_TYPE) - quals = cp_type_quals (type); - - dummy = build_decl (TYPE_DECL, NULL_TREE, type); - grok_method_quals (declarator->u.pointer.class_type, - dummy, quals); - type = TREE_TYPE (dummy); - quals = TYPE_UNQUALIFIED; + memfn_quals |= cp_type_quals (type); + type = build_memfn_type (type, + declarator->u.pointer.class_type, + memfn_quals); + memfn_quals = TYPE_UNQUALIFIED; } if (declarator->kind == cdk_reference) @@ -7743,9 +7756,7 @@ grokdeclarator (const cp_declarator *declarator, are always static functions. */ ; else - type = build_method_type_directly (ctype, - TREE_TYPE (type), - TYPE_ARG_TYPES (type)); + type = build_memfn_type (type, ctype, memfn_quals); } else if (declspecs->specs[(int)ds_typedef] && current_class_type) @@ -7837,6 +7848,18 @@ grokdeclarator (const cp_declarator *declarator, in typenames, fields or parameters. */ if (current_lang_name == lang_name_java) TYPE_FOR_JAVA (type) = 1; + + /* This declaration: + + typedef void f(int) const; + + declares a function type which is not a member of any + particular class, but which is cv-qualified; for + example "f S::*" declares a pointer to a const-qualified + member function of S. We record the cv-qualification in the + function type. */ + if (memfn_quals && TREE_CODE (type) == FUNCTION_TYPE) + type = cp_build_qualified_type (type, memfn_quals); if (decl_context == FIELD) decl = build_lang_decl (TYPE_DECL, unqualified_id, type); @@ -7898,26 +7921,17 @@ grokdeclarator (const cp_declarator *declarator, type with external linkage have external linkage. */ } - if (quals) - { - if (ctype == NULL_TREE) - { - if (TREE_CODE (type) == METHOD_TYPE) - ctype = TYPE_METHOD_BASETYPE (type); - /* Any qualifiers on a function type typedef have - already been dealt with. */ - else if (TREE_CODE (type) == FUNCTION_TYPE) - quals = TYPE_UNQUALIFIED; - } - if (ctype != NULL_TREE) - grok_method_quals (ctype, decl, quals); - } + /* Any qualifiers on a function type typedef have already been + dealt with. */ + if (memfn_quals && !ctype && TREE_CODE (type) == FUNCTION_TYPE) + memfn_quals = TYPE_UNQUALIFIED; if (signed_p || (typedef_decl && C_TYPEDEF_EXPLICITLY_SIGNED (typedef_decl))) C_TYPEDEF_EXPLICITLY_SIGNED (decl) = 1; - bad_specifiers (decl, "type", virtualp, quals != TYPE_UNQUALIFIED, + bad_specifiers (decl, "type", virtualp, + memfn_quals != TYPE_UNQUALIFIED, inlinep, friendp, raises != NULL_TREE); return decl; @@ -7965,7 +7979,7 @@ grokdeclarator (const cp_declarator *declarator, /* The qualifiers on the function type become the qualifiers on the non-static member function. */ - quals |= cp_type_quals (type); + memfn_quals |= cp_type_quals (type); } } @@ -8022,7 +8036,7 @@ grokdeclarator (const cp_declarator *declarator, type = void_type_node; } } - else if (quals) + else if (memfn_quals) { if (ctype == NULL_TREE) { @@ -8032,11 +8046,7 @@ grokdeclarator (const cp_declarator *declarator, ctype = TYPE_METHOD_BASETYPE (type); } if (ctype) - { - tree dummy = build_decl (TYPE_DECL, unqualified_id, type); - grok_method_quals (ctype, dummy, quals); - type = TREE_TYPE (dummy); - } + type = build_memfn_type (type, ctype, memfn_quals); } return type; @@ -8094,7 +8104,8 @@ grokdeclarator (const cp_declarator *declarator, { decl = cp_build_parm_decl (unqualified_id, type); - bad_specifiers (decl, "parameter", virtualp, quals != TYPE_UNQUALIFIED, + bad_specifiers (decl, "parameter", virtualp, + memfn_quals != TYPE_UNQUALIFIED, inlinep, friendp, raises != NULL_TREE); } else if (decl_context == FIELD) @@ -8156,9 +8167,7 @@ grokdeclarator (const cp_declarator *declarator, } } else if (staticp < 2) - type = build_method_type_directly (ctype, - TREE_TYPE (type), - TYPE_ARG_TYPES (type)); + type = build_memfn_type (type, ctype, memfn_quals); } /* Check that the name used for a destructor makes sense. */ @@ -8193,7 +8202,7 @@ grokdeclarator (const cp_declarator *declarator, ? unqualified_id : dname, parms, unqualified_id, - virtualp, flags, quals, raises, + virtualp, flags, memfn_quals, raises, friendp ? -1 : 0, friendp, publicp, inlinep, sfk, funcdef_flag, template_count, in_namespace, attrlist); @@ -8241,7 +8250,7 @@ grokdeclarator (const cp_declarator *declarator, ? unqualified_id : dname, parms, unqualified_id, - virtualp, flags, quals, raises, + virtualp, flags, memfn_quals, raises, friendp ? -1 : 0, friendp, 1, 0, sfk, funcdef_flag, template_count, in_namespace, attrlist); @@ -8298,7 +8307,8 @@ grokdeclarator (const cp_declarator *declarator, } decl = do_friend (ctype, unqualified_id, decl, - *attrlist, flags, quals, funcdef_flag); + *attrlist, flags, + funcdef_flag); return decl; } else @@ -8377,7 +8387,8 @@ grokdeclarator (const cp_declarator *declarator, } } - bad_specifiers (decl, "field", virtualp, quals != TYPE_UNQUALIFIED, + bad_specifiers (decl, "field", virtualp, + memfn_quals != TYPE_UNQUALIFIED, inlinep, friendp, raises != NULL_TREE); } } @@ -8438,7 +8449,7 @@ grokdeclarator (const cp_declarator *declarator, || storage_class != sc_static); decl = grokfndecl (ctype, type, original_name, parms, unqualified_id, - virtualp, flags, quals, raises, + virtualp, flags, memfn_quals, raises, 1, friendp, publicp, inlinep, sfk, funcdef_flag, template_count, in_namespace, attrlist); @@ -8481,7 +8492,8 @@ grokdeclarator (const cp_declarator *declarator, initialized, (type_quals & TYPE_QUAL_CONST) != 0, ctype ? ctype : in_namespace); - bad_specifiers (decl, "variable", virtualp, quals != TYPE_UNQUALIFIED, + bad_specifiers (decl, "variable", virtualp, + memfn_quals != TYPE_UNQUALIFIED, inlinep, friendp, raises != NULL_TREE); if (ctype) diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index ed26db2fe34..fe5db71c653 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -103,33 +103,28 @@ tree static_ctors; tree static_dtors; -/* Incorporate `const' and `volatile' qualifiers for member functions. - FUNCTION is a TYPE_DECL or a FUNCTION_DECL. - QUALS is a list of qualifiers. Returns any explicit - top-level qualifiers of the method's this pointer, anything other than - TYPE_UNQUALIFIED will be an extension. */ - -int -grok_method_quals (tree ctype, tree function, cp_cv_quals quals) + +/* Return a member function type (a METHOD_TYPE), given FNTYPE (a + FUNCTION_TYPE), CTYPE (class type), and QUALS (the cv-qualifiers + that apply to the function). */ + +tree +build_memfn_type (tree fntype, tree ctype, cp_cv_quals quals) { - tree fntype = TREE_TYPE (function); - tree raises = TYPE_RAISES_EXCEPTIONS (fntype); - int type_quals = TYPE_UNQUALIFIED; - int this_quals = TYPE_UNQUALIFIED; + tree raises; + int type_quals; type_quals = quals & ~TYPE_QUAL_RESTRICT; - this_quals = quals & TYPE_QUAL_RESTRICT; - ctype = cp_build_qualified_type (ctype, type_quals); fntype = build_method_type_directly (ctype, TREE_TYPE (fntype), (TREE_CODE (fntype) == METHOD_TYPE ? TREE_CHAIN (TYPE_ARG_TYPES (fntype)) : TYPE_ARG_TYPES (fntype))); + raises = TYPE_RAISES_EXCEPTIONS (fntype); if (raises) fntype = build_exception_variant (fntype, raises); - TREE_TYPE (function) = fntype; - return this_quals; + return fntype; } /* Build a PARM_DECL with NAME and TYPE, and set DECL_ARG_TYPE @@ -149,7 +144,7 @@ cp_build_parm_decl (tree name, tree type) /* Returns a PARM_DECL for a parameter of the indicated TYPE, with the indicated NAME. */ -static tree +tree build_artificial_parm (tree name, tree type) { tree parm = cp_build_parm_decl (name, type); @@ -257,11 +252,9 @@ maybe_retrofit_in_chrg (tree fn) QUALS are the qualifiers for the this pointer. */ void -grokclassfn (tree ctype, tree function, enum overload_flags flags, - cp_cv_quals quals) +grokclassfn (tree ctype, tree function, enum overload_flags flags) { tree fn_name = DECL_NAME (function); - cp_cv_quals this_quals = TYPE_UNQUALIFIED; /* Even within an `extern "C"' block, members get C++ linkage. See [dcl.link] for details. */ @@ -274,28 +267,6 @@ grokclassfn (tree ctype, tree function, enum overload_flags flags, DECL_NAME (function) = fn_name; } - if (quals) - this_quals = grok_method_quals (ctype, function, quals); - - if (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE) - { - /* Must add the class instance variable up front. */ - /* Right now we just make this a pointer. But later - we may wish to make it special. */ - tree type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (function))); - tree qual_type; - tree parm; - - /* The `this' parameter is implicitly `const'; it cannot be - assigned to. */ - this_quals |= TYPE_QUAL_CONST; - qual_type = cp_build_qualified_type (type, this_quals); - parm = build_artificial_parm (this_identifier, qual_type); - cp_apply_type_quals_to_decl (this_quals, parm); - TREE_CHAIN (parm) = DECL_ARGUMENTS (function); - DECL_ARGUMENTS (function) = parm; - } - DECL_CONTEXT (function) = ctype; if (flags == DTOR_FLAG) diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c index 0ae9130b61a..ac73e5f4787 100644 --- a/gcc/cp/friend.c +++ b/gcc/cp/friend.c @@ -399,15 +399,11 @@ make_friend_class (tree type, tree friend_type, bool complain) DECL is the FUNCTION_DECL that the friend is. - FLAGS is just used for `grokclassfn'. - - QUALS say what special qualifies should apply to the object - pointed to by `this'. */ + FLAGS is just used for `grokclassfn'. */ tree do_friend (tree ctype, tree declarator, tree decl, tree attrlist, enum overload_flags flags, - cp_cv_quals quals, bool funcdef_flag) { /* Every decl that gets here is a friend of something. */ @@ -456,8 +452,7 @@ do_friend (tree ctype, tree declarator, tree decl, if (flags == NO_SPECIAL && declarator == cname) DECL_CONSTRUCTOR_P (decl) = 1; - /* This will set up DECL_ARGUMENTS for us. */ - grokclassfn (ctype, decl, flags, quals); + grokclassfn (ctype, decl, flags); if (friend_depth) { diff --git a/gcc/cp/method.c b/gcc/cp/method.c index d2bee99dc5e..68ec8ab4181 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -978,6 +978,7 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) tree fn_type; tree raises = empty_except_spec; tree rhs_parm_type = NULL_TREE; + tree this_parm; tree name; HOST_WIDE_INT saved_processing_template_decl; @@ -1067,8 +1068,7 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) DECL_ASSIGNMENT_OPERATOR_P (fn) = 1; SET_OVERLOADED_OPERATOR_CODE (fn, NOP_EXPR); } - /* Create the argument list. The call to "grokclassfn" will add the - "this" parameter and any other implicit parameters. */ + /* Create the explicit arguments. */ if (rhs_parm_type) { /* Note that this parameter is *not* marked DECL_ARTIFICIAL; we @@ -1077,9 +1077,12 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p) DECL_ARGUMENTS (fn) = cp_build_parm_decl (NULL_TREE, rhs_parm_type); TREE_READONLY (DECL_ARGUMENTS (fn)) = 1; } + /* Add the "this" parameter. */ + this_parm = build_this_parm (fn_type, TYPE_UNQUALIFIED); + TREE_CHAIN (this_parm) = DECL_ARGUMENTS (fn); + DECL_ARGUMENTS (fn) = this_parm; - grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL, - TYPE_UNQUALIFIED); + grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL); grok_special_member_properties (fn); set_linkage_according_to_type (type, fn); rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6e05f64362d..535d2f5b632 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2006-04-23 Mark Mitchell + + PR c++/26912 + * g++.dg/template/friend41.C: New test. + 2006-04-23 David Edelsohn * g++.dg/opt/pr15551.C: Include cstdio. diff --git a/gcc/testsuite/g++.dg/template/friend41.C b/gcc/testsuite/g++.dg/template/friend41.C new file mode 100644 index 00000000000..6d686019d9a --- /dev/null +++ b/gcc/testsuite/g++.dg/template/friend41.C @@ -0,0 +1,11 @@ +// PR c++/26912 + +struct Foo { + template int func() const; +}; + +class Bar { + friend int Foo::func() const; +}; + + -- 2.11.4.GIT