From 563dd08adfa12573fdecf9669fdf685fef5fd360 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Sat, 8 Jul 2000 02:18:25 +0000 Subject: [PATCH] cpphash.c (is__va_args__): New function. * cpphash.c (is__va_args__): New function. (count_params): Fix line reported in error messages. Use is__va_args__. Don't return ')' on error. Flag GNU style rest args macro definitions. (parse_define): Check macro name is not __VA_ARGS__. (save_expansion): Check identifier in non-varargs-macro is not __VA_ARGS__. Don't flag GNU_VARARGS. * cpplex.c (parse_args): Accept no argument iff GNU_REST_ARGS. (maybe_paste_with_next): Use per-macro GNU_REST_ARGS rather than per-token GNU_VARARGS. * cpplib.h (GNU_VARARGS): Remove. (GNU_REST_ARGS): New. * gcc.dg/cpp/macsyntx.c: New tests. From-SVN: r34919 --- gcc/ChangeLog | 15 +++++ gcc/cpphash.c | 85 +++++++++++++++++++---------- gcc/cpplex.c | 12 ++-- gcc/cpplib.h | 4 +- gcc/testsuite/ChangeLog | 4 ++ gcc/testsuite/gcc.dg/cpp/macsyntx.c | 72 ++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 35 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/cpp/macsyntx.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index decd31face4..eaee6c8ac7d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2000-07-08 Neil Booth + + * cpphash.c (is__va_args__): New function. + (count_params): Fix line reported in error messages. Use + is__va_args__. Don't return ')' on error. Flag GNU style + rest args macro definitions. + (parse_define): Check macro name is not __VA_ARGS__. + (save_expansion): Check identifier in non-varargs-macro is + not __VA_ARGS__. Don't flag GNU_VARARGS. + * cpplex.c (parse_args): Accept no argument iff GNU_REST_ARGS. + (maybe_paste_with_next): Use per-macro GNU_REST_ARGS rather + than per-token GNU_VARARGS. + * cpplib.h (GNU_VARARGS): Remove. + (GNU_REST_ARGS): New. + Sat Jul 8 01:38:25 MET DST 2000 Jan Hubicka * i386.md (call_pop, call, call_value_pop): Do not set diff --git a/gcc/cpphash.c b/gcc/cpphash.c index 2007c52d661..ee0521b5977 100644 --- a/gcc/cpphash.c +++ b/gcc/cpphash.c @@ -52,6 +52,7 @@ static void dump_funlike_macro PARAMS ((cpp_reader *, cpp_hashnode *)); static const cpp_token *count_params PARAMS ((cpp_reader *, const cpp_token *, cpp_toklist *)); +static int is__va_args__ PARAMS ((cpp_reader *, const cpp_token *)); static cpp_toklist *parse_define PARAMS((cpp_reader *)); static int check_macro_redefinition PARAMS((cpp_reader *, cpp_hashnode *hp, const cpp_toklist *)); @@ -197,6 +198,26 @@ find_param (first, token) return 0; } +/* Constraint 6.10.3.5: __VA_ARGS__ should only appear in the + replacement list of a variable-arguments macro. TOKEN is assumed + to be of type CPP_NAME. */ +static int +is__va_args__ (pfile, token) + cpp_reader *pfile; + const cpp_token *token; +{ + if (!CPP_OPTION (pfile, pedantic) + || token->val.name.len != sizeof (var_args_str) - 1 + || ustrncmp (token->val.name.text, var_args_str, + sizeof (var_args_str) - 1)) + return 0; + + cpp_pedwarn_with_line (pfile, token->line, token->col, + "\"%s\" is only valid in the replacement list of a function-like macro", + var_args_str); + return 1; +} + /* Counts the parameters to a function like macro, and saves their spellings if necessary. Returns the token that we stopped scanning at; if it's type isn't CPP_CLOSE_PAREN there was an error, which @@ -208,7 +229,6 @@ count_params (pfile, first, list) cpp_toklist *list; { unsigned int params_len = 0, prev_ident = 0; - unsigned int line = pfile->token_list.line; const cpp_token *token, *temp; list->paramc = 0; @@ -217,7 +237,8 @@ count_params (pfile, first, list) switch (token->type) { case CPP_EOF: - cpp_error_with_line (pfile, line, token->col, + missing_paren: + cpp_error_with_line (pfile, token->line, token->col, "missing ')' in macro parameter list"); goto out; @@ -227,21 +248,14 @@ count_params (pfile, first, list) case CPP_NAME: if (prev_ident) { - cpp_error_with_line (pfile, line, token->col, + cpp_error_with_line (pfile, token->line, token->col, "macro parameters must be comma-separated"); goto out; } /* Constraint 6.10.3.5 */ - if (token->val.name.len == sizeof (var_args_str) - 1 - && !ustrncmp (token->val.name.text, var_args_str, - sizeof (var_args_str) - 1)) - { - cpp_error_with_line (pfile, line, token->col, - "\"%s\" is not a valid parameter name", - var_args_str); - goto out; - } + if (is__va_args__ (pfile, token)) + goto out; params_len += token->val.name.len + 1; prev_ident = 1; @@ -250,7 +264,7 @@ count_params (pfile, first, list) /* Constraint 6.10.3.6 - duplicate parameter names. */ if (find_param (first, token)) { - cpp_error_with_line (pfile, line, token->col, + cpp_error_with_line (pfile, token->line, token->col, "duplicate macro parameter \"%.*s\"", (int) token->val.name.len, token->val.name.text); @@ -259,7 +273,7 @@ count_params (pfile, first, list) break; default: - cpp_error_with_line (pfile, line, token->col, + cpp_error_with_line (pfile, token->line, token->col, "illegal token in macro parameter list"); goto out; @@ -271,8 +285,10 @@ count_params (pfile, first, list) case CPP_COMMA: if (!prev_ident) { - cpp_error_with_line (pfile, line, token->col, - "missing parameter name"); + cpp_error_with_line (pfile, token->line, token->col, + "parameter name expected"); + if (token->type == CPP_CLOSE_PAREN) + token--; /* Return the ',' not ')'. */ goto out; } prev_ident = 0; @@ -293,22 +309,24 @@ count_params (pfile, first, list) tok->val.name.len = sizeof (var_args_str) - 1; tok->val.name.text = var_args_str; /* Safe. */ list->paramc++; - + if (CPP_PEDANTIC (pfile) && ! CPP_OPTION (pfile, c99)) cpp_pedwarn (pfile, "C89 does not permit anon varargs macros"); } - else if (CPP_PEDANTIC (pfile)) - cpp_pedwarn (pfile, - "ISO C does not permit named varargs parameters"); + else + { + list->flags |= GNU_REST_ARGS; + if (CPP_PEDANTIC (pfile)) + cpp_pedwarn (pfile, + "ISO C does not permit named varargs parameters"); + } list->flags |= VAR_ARGS; token++; if (token->type == CPP_CLOSE_PAREN) goto scanned; - cpp_error_with_line (pfile, line, token->col, - "')' expected after \"...\""); - goto out; + goto missing_paren; } } @@ -345,8 +363,15 @@ parse_define (pfile) cpp_toklist *list; int prev_white = 0; - while ((token = cpp_get_token (pfile))->type == CPP_COMMENT) - prev_white = 1; + /* The first token after the macro's name. */ + token = cpp_get_token (pfile); + + /* Constraint 6.10.3.5 */ + if (is__va_args__ (pfile, token - 1)) + return 0; + + while (token->type == CPP_COMMENT) + token++, prev_white = 1; /* Allocate the expansion's list. It will go in the hash table. */ list = (cpp_toklist *) xmalloc (sizeof (cpp_toklist)); @@ -461,6 +486,12 @@ save_expansion (pfile, list, first, first_param) } } } + else if (token->type == CPP_NAME) + { + /* Constraint 6.10.3.5 */ + if (!(list->flags & VAR_ARGS) && is__va_args__ (pfile, token)) + return 1; + } ntokens++; if (token_spellings[token->type].type > SPELL_NONE) len += token->val.name.len; @@ -497,10 +528,6 @@ save_expansion (pfile, list, first, first_param) dest->flags = token[-1].flags | STRINGIFY_ARG; else dest->flags = token->flags; /* Particularly PREV_WHITE. */ - - if ((int) param_no == list->paramc && list->flags & VAR_ARGS - && dest != list->tokens && dest[-1].flags & PASTE_LEFT) - dest[-1].flags |= GNU_VARARGS; dest++; continue; diff --git a/gcc/cpplex.c b/gcc/cpplex.c index 24e4712d561..03439fa7233 100644 --- a/gcc/cpplex.c +++ b/gcc/cpplex.c @@ -2222,9 +2222,9 @@ parse_args (pfile, hp, args) e.g. #define debug(format, args...) ... debug("string"); This is exactly the same as if the rest argument had received no - tokens - debug("string",); */ + tokens - debug("string",); This extension is deprecated. */ - if (argc + 1 == macro->paramc && (macro->flags & VAR_ARGS)) + if (argc + 1 == macro->paramc && (macro->flags & GNU_REST_ARGS)) { /* Duplicate the placemarker. Then we can set its flags and position and safely be using more than one. */ @@ -2525,15 +2525,19 @@ maybe_paste_with_next (pfile, token) second = cpp_get_token (pfile); pfile->paste_level = 0; - /* Ignore placemarker argument tokens. */ + /* Ignore placemarker argument tokens (cannot be from an empty macro + since macros are not expanded). */ if (token->type == CPP_PLACEMARKER) pasted = duplicate_token (pfile, second); else if (second->type == CPP_PLACEMARKER) { + cpp_context *mac_context = CURRENT_CONTEXT (pfile) - 1; /* GCC has special extended semantics for a ## b where b is a varargs parameter: a disappears if b consists of no tokens. This extension is deprecated. */ - if (token->flags & GNU_VARARGS) + if ((mac_context->u.list->flags & GNU_REST_ARGS) + && (mac_context->u.list->tokens[mac_context->posn - 1].val.aux + 1 + == (unsigned) mac_context->u.list->paramc)) { cpp_warning (pfile, "deprecated GNU ## extension used"); pasted = duplicate_token (pfile, second); diff --git a/gcc/cpplib.h b/gcc/cpplib.h index 1c516a67312..e9bb24f8ee0 100644 --- a/gcc/cpplib.h +++ b/gcc/cpplib.h @@ -158,7 +158,6 @@ struct cpp_name #define STRINGIFY_ARG (1 << 3) /* If macro argument to be stringified. */ #define PASTE_LEFT (1 << 4) /* If on LHS of a ## operator. */ #define PASTED (1 << 5) /* The result of a ## operator. */ -#define GNU_VARARGS (1 << 6) /* GNU ## kludge. */ /* A preprocessing token. This has been carefully packed and should occupy 16 bytes on 32-bit hosts and 24 bytes on 64-bit hosts. */ @@ -181,7 +180,8 @@ struct cpp_token /* cpp_toklist flags. */ #define LIST_OFFSET (1 << 0) #define VAR_ARGS (1 << 1) -#define BEG_OF_FILE (1 << 2) +#define GNU_REST_ARGS (1 << 2) /* Set in addition to VAR_ARGS. */ +#define BEG_OF_FILE (1 << 3) struct directive; /* These are deliberately incomplete. */ struct answer; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 096cea1e456..4410e2e5526 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2000-07-08 Neil Booth + + * gcc.dg/cpp/macsyntx.c: New tests. + 2000-07-07 Jakub Jelinek * gcc.dg/20000707-1.c: New test. diff --git a/gcc/testsuite/gcc.dg/cpp/macsyntx.c b/gcc/testsuite/gcc.dg/cpp/macsyntx.c new file mode 100644 index 00000000000..baa69d1834a --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/macsyntx.c @@ -0,0 +1,72 @@ +/* Copyright (C) 2000 Free Software Foundation, Inc. */ + +/* { dg-do preprocess } */ +/* { dg-options -pedantic } */ + +/* Tests macro syntax, for both definition and invocation, including:- + + o Full range of macro definition semantics. + o No. of arguments supplied to function-like macros. + o Odd GNU rest args behaviour. + o Macro arguments do not flow into the rest of the file. */ + + +/* Test basic macro definition syntax. The macros are all called + "foo" deliberately to provoke an (excess) redefinition warning in + case the macros succeed in being entered in the macro hash table + despite being an error. + + Split a couple of the lines to check that the errors appear on the + right line (i.e. are associated with the correct token). */ + +#define ; /* { dg-error "identifier" } */ +#define SEMI; /* { dg-warning "space" } */ +#define foo(X /* { dg-error "missing" } */ +#define foo\ +(X,) /* { dg-error "parameter name" } */ +#define foo(, X) /* { dg-error "parameter name" } */ +#define foo(X, X) /* { dg-error "duplicate" } */ +#define foo(X Y) /* { dg-error "comma" } */ +#define foo(() /* { dg-error "illegal token" } */ +#define foo(..., X) /* { dg-error "missing" } */ +#define foo \ +__VA_ARGS__ /* { dg-warning "__VA_ARGS__" } */ +#define foo(__VA_ARGS__) /* { dg-warning "__VA_ARGS__" } */ +#define foo(...) __VA_ARGS__ /* OK. */ +#define __VA_ARGS__ /* { dg-warning "__VA_ARGS__" } */ + +/* test # of supplied arguments. */ +#define none() +#define one(x) +#define two(x, y) +#define var0(...) +#define var1(x, ...) +none() /* OK. */ +none(ichi) /* { dg-error "too many" } */ +one() /* OK. */ +one(ichi) /* OK. */ +one(ichi\ +, ni) /* { dg-error "too many" } */ +two(ichi) /* { dg-error "insufficient" } */ +var0() /* OK. */ +var0(ichi) /* OK. */ +var1() /* { dg-error "insufficient" } */ +var1(ichi) /* { dg-error "insufficient" } */ +var1(ichi, ni) /* OK. */ + +/* This tests two deprecated oddities of GNU rest args - omitting a + comma is OK, and backtracking a token on pasting an empty rest + args. */ +#define rest(x, y...) x ## y /* { dg-warning "ISO C" } */ +rest(ichi,) /* { dg-warning "deprecated" } */ +rest(ichi) /* { dg-warning "deprecated" } */ +#if 23 != rest(2, 3) /* OK, no warning. */ +#error 23 != 23 !! +#endif + +/* Test that we don't allow arguments to flow into the rest of the + file. */ +#define half_invocation do_nowt(2 +#define do_nowt(x) x +half_invocation ) /* OK. */ +do_nowt (half_invocation)) /* { dg-error "unterminated invocation" } */ -- 2.30.2