This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
PATCH RFA: Possible fix for PR c++/11987
- From: Ian Lance Taylor <ian at airs dot com>
- To: Mark Mitchell <mark at codesourcery dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: 16 Sep 2005 23:45:24 -0700
- Subject: PATCH RFA: Possible fix for PR c++/11987
- References: <20050916054129.9289.qmail@gossamer.airs.com><432B1D3C.5050100@codesourcery.com> <m38xxwdas1.fsf@gossamer.airs.com><432B2AEA.8050806@codesourcery.com> <m3slw4bqeg.fsf@gossamer.airs.com><432B492C.5050404@codesourcery.com>
Mark Mitchell <mark@codesourcery.com> writes:
> IIRC, it's specifically the situation where the typedef is nested in
> another class. Now, it would sure be useful if I had the standardese to
> justify that, wouldn't it? :-) I'll see if I can find it.
Thanks. Here is an attempt to implement that. It correctly gives an
error for the test case in PR c++/11987, and the patch adds that test
case as gcc/testsuite/g++.dg/typedef1.C.
This patch tweaks several places in the parser in a simple way. It's
a brute force approach. At the time we know that we have a function
definition, it's too late to tell whether things are OK. So instead
we detect it along the way, which means that we have to pass the
information along through three data structures.
The basic data flow is:
1) In cp_parser_nested_name_specifier_opt, we detect the case where
the scope uses a cross-class typedef. We record it in a new field
in the cp_parser struct.
2) When we create a new CPP_NESTED_NAME_SPECIFIER in
cpp_parser_nested_name_specifier_opt, we copy the new cp_parser
field into a new cp_token field.
3) When we pick apart the CPP_NESTED_NAME_SPECIFIER token in
cp_parser_pre_parsed_nested_name_specifier, we copy the new
cp_token field back into the new cp_parser field.
4) If we see the new parser field set when we are defining a function,
we give an error.
This patch passes the g++ testsuite on i686-pc-linux-gnu. I'm running
the libstdc++-v3 testsuite now, along with a bootstrap. I'm sending
this patch in now because I am expecting comments suggesting changes.
Just in case there aren't any comments, OK for mainline if the testing
passes?
I don't see any reason to put this accepts-invalid patch into 3.4 or
4.0.
Ian
cp/ChangeLog:
2005-09-16 Ian Lance Taylor <ian@airs.com>
PR c++/11987
* parser.c (struct cp_token): Add scope_external_typedef_p.
(eof_token): Initialize new field.
(make_id_declarator): Initialize scope_external_typedef_p.
(struct cp_parser): Add scope_external_typedef_p.
(cp_parser_nested_name_specifier_opt): Set parser's
scope_external_typedef_p field if we find a typedef for a type
defined in a different class. When creating a
CPP_NESTED_NAME_SPECIFIER, set scope_external_typedef_p.
(cp_parser_pre_parsed_nested_name_specifier): Set parser's
scope_external_typedef_p field from token.
(cp_parser_init_declarator): Give an error on a function
definition if scope_external_typedef_p is set in declarator.
(cp_parser_direct_declarator): Set scope_external_typedef_p in new
id declarator.
(cp_parser_clear_scope): New static inline function.
(cp_parser_diagnose_invalid_type_name): Save parser's
scope_external_typedef_p field.
(cp_parser_id_expression): Likewise.
(cp_parser_nested_name_specifier_opt): Likewise.
(cp_parser_class_or_namespace_name): Likewise
(cp_parser_conversion_function_id): Likewise.
(cp_parser_enclosed_template_argument_list): Likewise.
(cp_parser_unqualified_id): Clear scope_external_typedef_p when we
clear scope.
(cp_parser_nested_name_specifier_opt): Call
cp_parser_clear_scope.
(cp_parser_nested_name_specifier): Likewise.
(cp_parser_postfix_dot_deref_expression): Likewise.
(cp_parser_elaborated_type_specifier): Likewise.
(cp_parser_ptr_operator): Likewise.
(cp_parser_member_declaration): Likewise.
(cp_parser_base_clause): Likewise.
(cp_parser_global_scope_opt): Likewise.
(cp_parser_single_declaration): Likewise.
(cp_parser_global_scope_opt): Clear scope_external_typedef_p.
* cp-tree.h (struct cp_declarator): In u.id, change sfk to
bitfield, and add scope_external_typedef_p bitfield.
testsuite/ChangeLog:
2005-09-16 Ian Lance Taylor <ian@airs.com>
PR c++/11987
* g++.dg/lookup/typedef1.C: New test.
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.1164
diff -p -u -r1.1164 cp-tree.h
--- cp/cp-tree.h 12 Sep 2005 19:53:57 -0000 1.1164
+++ cp/cp-tree.h 17 Sep 2005 06:09:52 -0000
@@ -3608,7 +3608,10 @@ struct cp_declarator {
tree unqualified_name;
/* If this is the name of a function, what kind of special
function (if any). */
- special_function_kind sfk;
+ ENUM_BITFIELD(special_function_kind) sfk : 8;
+ /* Whether qualifying_scope was defined using a typedef for a
+ type defined in a different class. */
+ unsigned scope_external_typedef_p : 1;
} id;
/* For functions. */
struct {
Index: cp/parser.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/parser.c,v
retrieving revision 1.358
diff -p -u -r1.358 parser.c
--- cp/parser.c 13 Sep 2005 02:41:04 -0000 1.358
+++ cp/parser.c 17 Sep 2005 06:09:54 -0000
@@ -59,6 +59,8 @@ typedef struct cp_token GTY (())
BOOL_BITFIELD in_system_header : 1;
/* True if this token is from a context where it is implicitly extern "C" */
BOOL_BITFIELD implicit_extern_c : 1;
+ /* Copy of scope_external_typedef_p for CPP_NESTED_NAME_SPECIFIER. */
+ BOOL_BITFIELD scope_external_typedef_p : 1;
/* The value associated with this token, if any. */
tree value;
/* The location at which this token was found. */
@@ -72,7 +74,7 @@ DEF_VEC_ALLOC_P (cp_token_position,heap)
static const cp_token eof_token =
{
- CPP_EOF, RID_MAX, 0, 0, 0, NULL_TREE,
+ CPP_EOF, RID_MAX, 0, 0, 0, 0, NULL_TREE,
#if USE_MAPPED_LOCATION
0
#else
@@ -843,6 +845,7 @@ make_id_declarator (tree qualifying_scop
declarator->u.id.qualifying_scope = qualifying_scope;
declarator->u.id.unqualified_name = unqualified_name;
declarator->u.id.sfk = sfk_none;
+ declarator->u.id.scope_external_typedef_p = false;
return declarator;
}
@@ -1253,6 +1256,10 @@ typedef struct cp_parser GTY(())
GNU extensions are not recognized. */
bool allow_gnu_extensions_p;
+ /* TRUE if the scope field was resolved using a typedef for a type
+ defined in a different class. */
+ bool scope_external_typedef_p;
+
/* TRUE if the `>' token should be interpreted as the greater-than
operator. FALSE if it is the end of a template-id or
template-parameter-list. */
@@ -1933,6 +1940,17 @@ cp_parser_simulate_error (cp_parser* par
return false;
}
+/* Clear the current parser scope. */
+
+static inline void
+cp_parser_clear_scope (cp_parser* parser)
+{
+ parser->scope = NULL;
+ parser->scope_external_typedef_p = false;
+ parser->qualifying_scope = NULL;
+ parser->object_scope = NULL;
+}
+
/* This function is called when a type is defined. If type
definitions are forbidden at this point, an error message is
issued. */
@@ -2037,11 +2055,15 @@ static void
cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree scope, tree id)
{
tree decl, old_scope;
+ bool old_scope_external_typedef_p;
+
/* Try to lookup the identifier. */
old_scope = parser->scope;
+ old_scope_external_typedef_p = parser->scope_external_typedef_p;
parser->scope = scope;
decl = cp_parser_lookup_name_simple (parser, id);
parser->scope = old_scope;
+ parser->scope_external_typedef_p = old_scope_external_typedef_p;
/* If the lookup found a template-name, it means that the user forgot
to specify an argument list. Emit a useful error message. */
if (TREE_CODE (decl) == TEMPLATE_DECL)
@@ -3126,6 +3148,7 @@ cp_parser_id_expression (cp_parser *pars
if (nested_name_specifier_p)
{
tree saved_scope;
+ bool saved_scope_external_typedef_p;
tree saved_object_scope;
tree saved_qualifying_scope;
tree unqualified_id;
@@ -3138,6 +3161,7 @@ cp_parser_id_expression (cp_parser *pars
/* Name lookup we do during the processing of the
unqualified-id might obliterate SCOPE. */
saved_scope = parser->scope;
+ saved_scope_external_typedef_p = parser->scope_external_typedef_p;
saved_object_scope = parser->object_scope;
saved_qualifying_scope = parser->qualifying_scope;
/* Process the final unqualified-id. */
@@ -3146,6 +3170,7 @@ cp_parser_id_expression (cp_parser *pars
declarator_p);
/* Restore the SAVED_SCOPE for our caller. */
parser->scope = saved_scope;
+ parser->scope_external_typedef_p = saved_scope_external_typedef_p;
parser->object_scope = saved_object_scope;
parser->qualifying_scope = saved_qualifying_scope;
@@ -3380,6 +3405,7 @@ cp_parser_unqualified_id (cp_parser* par
if (!done)
{
parser->scope = NULL_TREE;
+ parser->scope_external_typedef_p = false;
parser->object_scope = NULL_TREE;
parser->qualifying_scope = NULL_TREE;
type_decl
@@ -3502,6 +3528,7 @@ cp_parser_nested_name_specifier_opt (cp_
{
tree new_scope;
tree old_scope;
+ bool old_scope_external_typedef_p;
tree saved_qualifying_scope;
bool template_keyword_p;
@@ -3557,6 +3584,7 @@ cp_parser_nested_name_specifier_opt (cp_
/* Save the old scope since the name lookup we are about to do
might destroy it. */
old_scope = parser->scope;
+ old_scope_external_typedef_p = parser->scope_external_typedef_p;
saved_qualifying_scope = parser->qualifying_scope;
/* In a declarator-id like "X<T>::I::Y<T>" we must be able to
look up names in "X<T>::I" in order to determine that "Y" is
@@ -3589,6 +3617,7 @@ cp_parser_nested_name_specifier_opt (cp_
failed attempt at finding the last
class-or-namespace-name. */
parser->scope = old_scope;
+ parser->scope_external_typedef_p = old_scope_external_typedef_p;
parser->qualifying_scope = saved_qualifying_scope;
/* If the next token is an identifier, and the one after
that is a `::', then any valid interpretation would have
@@ -3611,7 +3640,7 @@ cp_parser_nested_name_specifier_opt (cp_
cp_parser_name_lookup_error
(parser, token->value, decl,
"is not a class or namespace");
- parser->scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
error_p = true;
/* Treat this as a successful nested-name-specifier
due to:
@@ -3630,11 +3659,32 @@ cp_parser_nested_name_specifier_opt (cp_
/* We've found one valid nested-name-specifier. */
success = true;
+
/* Make sure we look in the right scope the next time through
the loop. */
- parser->scope = (TREE_CODE (new_scope) == TYPE_DECL
- ? TREE_TYPE (new_scope)
- : new_scope);
+ if (TREE_CODE (new_scope) != TYPE_DECL)
+ parser->scope = new_scope;
+ else
+ {
+ parser->scope = TREE_TYPE (new_scope);
+
+ /* We want to prohibit the use of some typedefs for a
+ function definition, so look for them here. The case we
+ want to prohibit is a typedef found within a class which
+ names a type defined in a different class. At this point
+ we don't know if we are going to see a function
+ definition, so just record the fact that we saw a
+ typedef. */
+ if (!DECL_IMPLICIT_TYPEDEF_P (new_scope)
+ && !DECL_ARTIFICIAL (new_scope)
+ && TYPE_CONTEXT (parser->scope)
+ && CLASS_TYPE_P (TYPE_CONTEXT (parser->scope))
+ && DECL_CONTEXT (new_scope)
+ && CLASS_TYPE_P (DECL_CONTEXT (new_scope))
+ && TYPE_CONTEXT (parser->scope) != DECL_CONTEXT (new_scope))
+ parser->scope_external_typedef_p = true;
+ }
+
/* If it is a class scope, try to complete it; we are about to
be looking up names inside the class. */
if (TYPE_P (parser->scope)
@@ -3663,6 +3713,7 @@ cp_parser_nested_name_specifier_opt (cp_
token->type = CPP_NESTED_NAME_SPECIFIER;
token->value = build_tree_list (access_check, parser->scope);
TREE_TYPE (token->value) = parser->qualifying_scope;
+ token->scope_external_typedef_p = parser->scope_external_typedef_p;
token->keyword = RID_MAX;
/* Purge all subsequent tokens. */
@@ -3697,7 +3748,7 @@ cp_parser_nested_name_specifier (cp_pars
if (!scope)
{
cp_parser_error (parser, "expected nested-name-specifier");
- parser->scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
}
return scope;
@@ -3729,6 +3780,7 @@ cp_parser_class_or_namespace_name (cp_pa
bool is_declaration)
{
tree saved_scope;
+ bool saved_scope_external_typedef_p;
tree saved_qualifying_scope;
tree saved_object_scope;
tree scope;
@@ -3738,6 +3790,7 @@ cp_parser_class_or_namespace_name (cp_pa
current PARSER->SCOPE since cp_parser_class_name will destroy
it. */
saved_scope = parser->scope;
+ saved_scope_external_typedef_p = parser->scope_external_typedef_p;
saved_qualifying_scope = parser->qualifying_scope;
saved_object_scope = parser->object_scope;
/* Try for a class-name first. If the SAVED_SCOPE is a type, then
@@ -3757,6 +3810,7 @@ cp_parser_class_or_namespace_name (cp_pa
{
/* Restore the saved scope. */
parser->scope = saved_scope;
+ parser->scope_external_typedef_p = saved_scope_external_typedef_p;
parser->qualifying_scope = saved_qualifying_scope;
parser->object_scope = saved_object_scope;
/* If we are not looking at an identifier followed by the scope
@@ -4400,9 +4454,7 @@ cp_parser_postfix_dot_deref_expression (
/* Check to see whether or not the expression is type-dependent. */
dependent_p = type_dependent_expression_p (postfix_expression);
/* The identifier following the `->' or `.' is not qualified. */
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
*idk = CP_ID_KIND_NONE;
/* Enter the scope corresponding to the type of the object
given by the POSTFIX_EXPRESSION. */
@@ -4494,9 +4546,7 @@ cp_parser_postfix_dot_deref_expression (
if (name != error_mark_node && !BASELINK_P (name) && parser->scope)
{
name = build_nt (SCOPE_REF, parser->scope, name);
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
}
if (scope && name && BASELINK_P (name))
adjust_result_of_qualified_name_lookup
@@ -7624,6 +7674,7 @@ cp_parser_conversion_function_id (cp_par
{
tree type;
tree saved_scope;
+ bool saved_scope_external_typedef_p;
tree saved_qualifying_scope;
tree saved_object_scope;
tree pushed_scope = NULL_TREE;
@@ -7635,6 +7686,7 @@ cp_parser_conversion_function_id (cp_par
reset. However, we need that information in able to look up the
conversion function later, so we save it here. */
saved_scope = parser->scope;
+ saved_scope_external_typedef_p = parser->scope_external_typedef_p;
saved_qualifying_scope = parser->qualifying_scope;
saved_object_scope = parser->object_scope;
/* We must enter the scope of the class so that the names of
@@ -7659,6 +7711,7 @@ cp_parser_conversion_function_id (cp_par
pop_scope (pushed_scope);
/* Restore the saved scope. */
parser->scope = saved_scope;
+ parser->scope_external_typedef_p = saved_scope_external_typedef_p;
parser->qualifying_scope = saved_qualifying_scope;
parser->object_scope = saved_object_scope;
/* If the TYPE is invalid, indicate failure. */
@@ -9937,7 +9990,7 @@ cp_parser_elaborated_type_specifier (cp_
if (identifier == error_mark_node)
{
- parser->scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
return error_mark_node;
}
@@ -10770,12 +10823,24 @@ cp_parser_init_declarator (cp_parser* pa
}
else
{
+ cp_declarator *pd;
+
/* Neither attributes nor an asm-specification are allowed
on a function-definition. */
if (asm_specification)
error ("an asm-specification is not allowed on a function-definition");
if (attributes)
error ("attributes are not allowed on a function-definition");
+
+ /* A function definition must be written using the real
+ name, so if we used a typedef to get here something has
+ gone wrong. */
+ pd = declarator;
+ while (pd && pd->kind != cdk_id)
+ pd = pd->declarator;
+ if (pd && pd->u.id.scope_external_typedef_p)
+ error ("invalid typedef used in function name");
+
/* This is a function-definition. */
*function_definition_p = true;
@@ -11370,6 +11435,8 @@ cp_parser_direct_declarator (cp_parser*
declarator = make_id_declarator (qualifying_scope,
unqualified_name);
+ declarator->u.id.scope_external_typedef_p =
+ parser->scope_external_typedef_p;
declarator->id_loc = token->location;
if (unqualified_name)
{
@@ -11525,9 +11592,7 @@ cp_parser_ptr_operator (cp_parser* parse
current SCOPE. */
*type = parser->scope;
/* The next name will not be qualified. */
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
/* Indicate that the `*' operator was used. */
code = INDIRECT_REF;
/* Look for the optional cv-qualifier-seq. */
@@ -13525,9 +13590,7 @@ cp_parser_member_declaration (cp_parser*
/* If there is any qualification still in effect, clear it
now; we will be starting fresh with the next declarator. */
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
/* If it's a `,', then there are more declarators. */
if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
cp_lexer_consume_token (parser->lexer);
@@ -13679,9 +13742,7 @@ cp_parser_base_clause (cp_parser* parser
/* PARSER->SCOPE may still be non-NULL at this point, if the last
base class had a qualified name. However, the next name that
appears is certainly not qualified. */
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
return nreverse (bases);
}
@@ -14834,17 +14895,14 @@ cp_parser_global_scope_opt (cp_parser* p
cp_lexer_consume_token (parser->lexer);
/* Set the SCOPE so that we know where to start the lookup. */
parser->scope = global_namespace;
+ parser->scope_external_typedef_p = false;
parser->qualifying_scope = global_namespace;
parser->object_scope = NULL_TREE;
return parser->scope;
}
else if (!current_scope_valid_p)
- {
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
- }
+ cp_parser_clear_scope (parser);
return NULL_TREE;
}
@@ -15291,9 +15349,7 @@ cp_parser_single_declaration (cp_parser*
/* Clear any current qualification; whatever comes next is the start
of something new. */
- parser->scope = NULL_TREE;
- parser->qualifying_scope = NULL_TREE;
- parser->object_scope = NULL_TREE;
+ cp_parser_clear_scope (parser);
/* Look for a trailing `;' after the declaration. */
if (!function_definition_p
&& (decl == error_mark_node
@@ -15408,6 +15464,7 @@ cp_parser_enclosed_template_argument_lis
{
tree arguments;
tree saved_scope;
+ bool saved_scope_external_typedef_p;
tree saved_qualifying_scope;
tree saved_object_scope;
bool saved_greater_than_is_operator_p;
@@ -15423,6 +15480,7 @@ cp_parser_enclosed_template_argument_lis
/* Parsing the argument list may modify SCOPE, so we save it
here. */
saved_scope = parser->scope;
+ saved_scope_external_typedef_p = parser->scope_external_typedef_p;
saved_qualifying_scope = parser->qualifying_scope;
saved_object_scope = parser->object_scope;
/* Parse the template-argument-list itself. */
@@ -15472,6 +15530,7 @@ cp_parser_enclosed_template_argument_lis
= saved_greater_than_is_operator_p;
/* Restore the SAVED_SCOPE. */
parser->scope = saved_scope;
+ parser->scope_external_typedef_p = saved_scope_external_typedef_p;
parser->qualifying_scope = saved_qualifying_scope;
parser->object_scope = saved_object_scope;
@@ -16077,16 +16136,19 @@ cp_parser_optional_template_keyword (cp_
static void
cp_parser_pre_parsed_nested_name_specifier (cp_parser *parser)
{
+ cp_token *token;
tree value;
tree check;
/* Get the stored value. */
- value = cp_lexer_consume_token (parser->lexer)->value;
+ token = cp_lexer_consume_token (parser->lexer);
+ value = token->value;
/* Perform any access checks that were deferred. */
for (check = TREE_PURPOSE (value); check; check = TREE_CHAIN (check))
perform_or_defer_access_check (TREE_PURPOSE (check), TREE_VALUE (check));
/* Set the scope from the stored value. */
parser->scope = TREE_VALUE (value);
+ parser->scope_external_typedef_p = token->scope_external_typedef_p;
parser->qualifying_scope = TREE_TYPE (value);
parser->object_scope = NULL_TREE;
}
Index: testsuite/g++.dg/lookup/typedef1.C
===================================================================
RCS file: testsuite/g++.dg/lookup/typedef1.C
diff -N testsuite/g++.dg/lookup/typedef1.C
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/g++.dg/lookup/typedef1.C 17 Sep 2005 06:10:04 -0000
@@ -0,0 +1,15 @@
+// PR 11987
+// { dg-do compile }
+
+template <int dim> struct X {
+ struct I { I(); };
+};
+
+template <int dim> struct Y : X<dim> {
+ typedef typename X<dim>::I I;
+};
+
+template <int dim>
+Y<dim>::I::I () {} // { dg-error "invalid typedef" }
+
+template struct Y<1>;