This adds the ability to defer the validation of numeric attribute arguments until the sequence is parsed if the attribute being handled is one known to be 'clang form'.
We do this by considering the arguments to be strings regardless of content and defer the interpretation of those strings until the argument processing. PR c++/109877 gcc/c-family/ChangeLog: * c-lex.cc (c_lex_with_flags): Allow for the case where we wish to defer interpretation of numeric values until parse time. * c-pragma.h (C_LEX_NUMBER_AS_STRING): New. gcc/c/ChangeLog: * c-parser.cc (struct c_parser): Provide a flag to notify that argument parsing should return attribute arguments as string constants. (c_lex_one_token): Act to defer numeric value validation. (c_parser_clang_attribute_arguments): New. (c_parser_gnu_attribute): Allow for clang-form GNU-style attributes. Signed-off-by: Iain Sandoe <i...@sandoe.co.uk> --- gcc/c-family/c-lex.cc | 15 ++++++ gcc/c-family/c-pragma.h | 3 ++ gcc/c/c-parser.cc | 109 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 122 insertions(+), 5 deletions(-) diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc index 06c2453c89a..d535f5b460c 100644 --- a/gcc/c-family/c-lex.cc +++ b/gcc/c-family/c-lex.cc @@ -533,6 +533,21 @@ c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags, case CPP_NUMBER: { + /* If the user wants number-like entities to be returned as a raw + string, then don't try to classify them, which emits unwanted + diagnostics. */ + if (lex_flags & C_LEX_NUMBER_AS_STRING) + { + /* build_string adds a trailing NUL at [len]. */ + tree num_string = build_string (tok->val.str.len + 1, + (const char *) tok->val.str.text); + TREE_TYPE (num_string) = char_array_type_node; + *value = num_string; + /* We will effectively note this as CPP_N_INVALID, because we + made no checks here. */ + break; + } + const char *suffix = NULL; unsigned int flags = cpp_classify_number (parse_in, tok, &suffix, *loc); diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index 98177913053..11cde74f9f0 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -276,6 +276,9 @@ extern void pragma_lex_discard_to_eol (); #define C_LEX_STRING_NO_JOIN 2 /* Do not concatenate strings nor translate them into execution character set. */ +#define C_LEX_NUMBER_AS_STRING 4 /* Do not classify a number, but + instead return it as a raw + string. */ /* This is not actually available to pragma parsers. It's merely a convenient location to declare this function for c-lex, after diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 703f9570dbc..aaaa16cc05d 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -217,6 +217,9 @@ struct GTY(()) c_parser { should translate them to the execution character set (false inside attributes). */ BOOL_BITFIELD translate_strings_p : 1; + /* True if we want to lex arbitrary number-like sequences as their + string representation. */ + BOOL_BITFIELD lex_number_as_string : 1; /* Objective-C specific parser/lexer information. */ @@ -308,10 +311,10 @@ c_lex_one_token (c_parser *parser, c_token *token, bool raw = false) if (raw || vec_safe_length (parser->raw_tokens) == 0) { + int lex_flags = parser->lex_joined_string ? 0 : C_LEX_STRING_NO_JOIN; + lex_flags |= parser->lex_number_as_string ? C_LEX_NUMBER_AS_STRING : 0; token->type = c_lex_with_flags (&token->value, &token->location, - &token->flags, - (parser->lex_joined_string - ? 0 : C_LEX_STRING_NO_JOIN)); + &token->flags, lex_flags); token->id_kind = C_ID_NONE; token->keyword = RID_MAX; token->pragma_kind = PRAGMA_NONE; @@ -5210,6 +5213,98 @@ c_parser_gnu_attribute_any_word (c_parser *parser) return attr_name; } +/* Handle parsing clang-form attribute arguments, where we need to adjust + the parsing rules to relate to a specific attribute. */ + +static tree +c_parser_clang_attribute_arguments (c_parser *parser, tree /*attr_id*/) +{ + /* We can, if required, alter the parsing on the basis of the attribute. + At present, we handle the availability attr, where ach entry can be : + identifier + identifier=N.MM.Z + identifier="string" + followed by ',' or ) for the last entry*/ + + tree attr_args = NULL_TREE; + if (c_parser_next_token_is (parser, CPP_NAME) + && c_parser_peek_token (parser)->id_kind == C_ID_ID + && c_parser_peek_2nd_token (parser)->type == CPP_COMMA) + { + tree platf = c_parser_peek_token (parser)->value; + c_parser_consume_token (parser); + attr_args = tree_cons (NULL_TREE, platf, NULL_TREE); + } + else + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected a platform name followed by %<,%>"); + return error_mark_node; + } + c_parser_consume_token (parser); /* consume the ',' */ + do + { + tree name = NULL_TREE; + tree value = NULL_TREE; + + if (c_parser_next_token_is (parser, CPP_NAME) + && c_parser_peek_token (parser)->id_kind == C_ID_ID) + { + name = c_parser_peek_token (parser)->value; + c_parser_consume_token (parser); + } + else + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected an attribute keyword"); + return error_mark_node; + } + if (c_parser_next_token_is (parser, CPP_EQ)) + { + c_parser_consume_token (parser); /* eat the '=' */ + /* We need to bludgeon the lexer into not trying to interpret the + xx.yy.zz form, since that just looks like a malformed float. + Also, as a result of macro processing, we can have strig literals + that are in multiple pieces so, for this specific part of the + parse, we need to join strings. */ + bool saved_join_state = parser->lex_joined_string; + parser->lex_number_as_string = 1; + parser->lex_joined_string = 1; + /* So look at the next token, expecting a string, or something that + looks initially like a number, but might be a version number. */ + c_parser_peek_token (parser); + /* Done with the funky number parsing. */ + parser->lex_number_as_string = 0; + parser->lex_joined_string = saved_join_state; + if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN) + && c_parser_next_token_is_not (parser, CPP_COMMA)) + { + value = c_parser_peek_token (parser)->value; + /* ???: check for error mark and early-return? */ + c_parser_consume_token (parser); + } + else + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected a value"); + return error_mark_node; + } + } + else if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN) + && c_parser_next_token_is_not (parser, CPP_COMMA)) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, + "expected %<,%> or %<=%>"); + return error_mark_node; + } + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); /* Just skip the comma. */ + tree t = tree_cons (value, name, NULL); + chainon (attr_args, t); + } while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)); + return attr_args; +} + /* Parse attribute arguments. This is a common form of syntax covering all currently valid GNU and standard attributes. @@ -5375,9 +5470,13 @@ c_parser_gnu_attribute (c_parser *parser, tree attrs, attrs = chainon (attrs, attr); return attrs; } - c_parser_consume_token (parser); + c_parser_consume_token (parser); /* The '('. */ - tree attr_args + tree attr_args; + if (attribute_clang_form_p (attr_name)) + attr_args = c_parser_clang_attribute_arguments (parser, attr_name); + else + attr_args = c_parser_attribute_arguments (parser, attribute_takes_identifier_p (attr_name), false, -- 2.39.2 (Apple Git-143)