To prevent PPH images from depend on the context in which they were generated, we require that the header file be compiled in isolation and when included, it should only be included in the global context. That is, things like
namespace Foo { #include "bar.h" ... }; should prevent bar.h from becoming a PPH image, since all the symbols defined in it would belong to Foo, but when bar.pph was generated, they belonged to :: This patch detects the use of PPH images inside nested scopes like that. It is a bit crude, but it works. During lexing, it keeps track of open and close braces (all kinds, not just { }), so when the #include command is found, it rejects the image if the nesting level is positive. Jason, I added a field to scope_chain. That seemed the cleaner approach to keep track of the nesting level. Is that a good place to keep track of it? This is the kind of bookkeeping that scope_chain seems to be used for, but I can put it elsewhere if you want. This error flagged the usage of sys/types.pph. This file is included inside 'extern "C" { }', so strictly speaking it should be rejected. We can relax these restrictions later. Tested on x86_64. Applied to branch. * cp-tree.h (struct saved_scope): Add field x_brace_nesting. * parser.c (cp_lexer_token_is_open_brace): New. (cp_lexer_token_is_close_brace): New. (cp_lexer_get_preprocessor_token): Call them. Increase scope_chain->x_brace_nesting for open braces, decrease for closing braces. * pph.c (pph_is_valid_here): New. Return false if scope_chain->x_brace_nesting is greater than 0. (pph_include_handler): Call it. testsuite/ChangeLog.pph * g++.dg/pph/pph.exp: Do not create a PPH image for sys/types.h. * g++.dg/pph/y8inc-nmspc.cc: Mark fixed. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8fb2ccc..e0b67c6 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -973,6 +973,10 @@ struct GTY(()) saved_scope { cp_binding_level *bindings; struct saved_scope *prev; + + /* Used during lexing to validate where PPH images are included, it + keeps track of nested bracing. */ + unsigned x_brace_nesting; }; /* The current open namespace. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 009922e..919671c 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -748,6 +748,31 @@ cp_lexer_saving_tokens (const cp_lexer* lexer) return VEC_length (cp_token_position, lexer->saved_tokens) != 0; } + +/* Return true if TOKEN is one of CPP_OPEN_SQUARE, CPP_OPEN_BRACE or + CPP_OPEN_PAREN. */ + +static inline bool +cp_lexer_token_is_open_brace (cp_token *token) +{ + return token->type == CPP_OPEN_SQUARE + || token->type == CPP_OPEN_BRACE + || token->type == CPP_OPEN_PAREN; +} + + +/* Return true if TOKEN is one of CPP_CLOSE_SQUARE, CPP_CLOSE_BRACE or + CPP_CLOSE_PAREN. */ + +static inline bool +cp_lexer_token_is_close_brace (cp_token *token) +{ + return token->type == CPP_CLOSE_SQUARE + || token->type == CPP_CLOSE_BRACE + || token->type == CPP_CLOSE_PAREN; +} + + /* Store the next token from the preprocessor in *TOKEN. Return true if we reach EOF. If LEXER is NULL, assume we are handling an initial #pragma pch_preprocess, and thus want the lexer to return @@ -835,8 +860,13 @@ cp_lexer_get_preprocessor_token (cp_lexer *lexer, cp_token *token) TREE_INT_CST_LOW (token->u.value)); token->u.value = NULL_TREE; } + else if (cp_lexer_token_is_open_brace (token)) + scope_chain->x_brace_nesting++; + else if (cp_lexer_token_is_close_brace (token)) + scope_chain->x_brace_nesting--; } + /* Update the globals input_location and the input file stack from TOKEN. */ static inline void cp_lexer_set_source_position_from_token (cp_token *token) diff --git a/gcc/cp/pph.c b/gcc/cp/pph.c index 9b1c63c..cbd9c24 100644 --- a/gcc/cp/pph.c +++ b/gcc/cp/pph.c @@ -94,11 +94,30 @@ pph_dump_namespace (FILE *file, tree ns) } +/* Return true if PPH image NAME can be used at the point of inclusion + (given by LOC). */ + +static bool +pph_is_valid_here (const char *name, location_t loc) +{ + /* If we are inside a scope, reject the image. We could be inside a + namespace or a structure which changes the parsing context for + the original text file. */ + if (scope_chain->x_brace_nesting > 0) + { + error_at (loc, "PPH file %s not included at global scope", name); + return false; + } + + return true; +} + + /* Record a #include or #include_next for PPH. */ static bool pph_include_handler (cpp_reader *reader, - location_t loc ATTRIBUTE_UNUSED, + location_t loc, const unsigned char *dname, const char *name, int angle_brackets, @@ -117,7 +136,9 @@ pph_include_handler (cpp_reader *reader, read_text_file_p = true; pph_file = query_pph_include_map (name); - if (pph_file != NULL && !cpp_included_before (reader, name, input_location)) + if (pph_file != NULL + && pph_is_valid_here (name, loc) + && !cpp_included_before (reader, name, input_location)) { /* Hack. We do this to mimic what the non-pph compiler does in _cpp_stack_include as our goal is to have identical line_tables. */ diff --git a/gcc/testsuite/g++.dg/pph/pph.exp b/gcc/testsuite/g++.dg/pph/pph.exp index 542cf3e..a632365 100644 --- a/gcc/testsuite/g++.dg/pph/pph.exp +++ b/gcc/testsuite/g++.dg/pph/pph.exp @@ -38,7 +38,6 @@ exec echo "math.h math.pph" >> pph.map exec echo "stdio.h stdio.pph" >> pph.map exec echo "stdlib.h stdlib.pph" >> pph.map exec echo "string.h string.pph" >> pph.map -exec echo "sys/types.h types.pph" >> pph.map set mapflag -fpph-map=pph.map diff --git a/gcc/testsuite/g++.dg/pph/y8inc-nmspc.cc b/gcc/testsuite/g++.dg/pph/y8inc-nmspc.cc index a8784f7..70b209a 100644 --- a/gcc/testsuite/g++.dg/pph/y8inc-nmspc.cc +++ b/gcc/testsuite/g++.dg/pph/y8inc-nmspc.cc @@ -1,4 +1,3 @@ namespace smother { -#include "x1struct1.h" -// { dg-error "pph file not included at global scope" "" { xfail *-*-* } } +#include "x1struct1.h" // { dg-error "PPH file .* not included at global scope" "" } } -- This patch is available for review at http://codereview.appspot.com/4958045