Commit 261f5379 by Olli Etuaho

Support parsing defined operator generated by macro expansion

dEQP tests enforce that the defined operator should be parsed even when it is generated as a result of macro expansion, even though this is undefined according to the C++ preprocessor spec. Implement support for this by putting the parsing for the defined operator inside MacroExpander. The operator gets processed right after it is generated by macro expansion. Parsing the defined operator is toggled with a boolean according to the context where MacroExpander is used. BUG=angleproject:989 TEST=angle_unittests, dEQP-GLES3.functional.shaders.preprocessor.* - 2 tests start passing: dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.basic_2* Change-Id: I780e63bd4558253657d898685d62339017564a06 Reviewed-on: https://chromium-review.googlesource.com/300970Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 9f2fd2b4
...@@ -141,71 +141,6 @@ bool isMacroPredefined(const std::string &name, ...@@ -141,71 +141,6 @@ bool isMacroPredefined(const std::string &name,
namespace pp namespace pp
{ {
class DefinedParser : public Lexer
{
public:
DefinedParser(Lexer *lexer,
const MacroSet *macroSet,
Diagnostics *diagnostics)
: mLexer(lexer),
mMacroSet(macroSet),
mDiagnostics(diagnostics)
{
}
protected:
void lex(Token *token) override
{
const char kDefined[] = "defined";
mLexer->lex(token);
if (token->type != Token::IDENTIFIER)
return;
if (token->text != kDefined)
return;
bool paren = false;
mLexer->lex(token);
if (token->type == '(')
{
paren = true;
mLexer->lex(token);
}
if (token->type != Token::IDENTIFIER)
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
token->location, token->text);
skipUntilEOD(mLexer, token);
return;
}
MacroSet::const_iterator iter = mMacroSet->find(token->text);
std::string expression = iter != mMacroSet->end() ? "1" : "0";
if (paren)
{
mLexer->lex(token);
if (token->type != ')')
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
token->location, token->text);
skipUntilEOD(mLexer, token);
return;
}
}
// We have a valid defined operator.
// Convert the current token into a CONST_INT token.
token->type = Token::CONST_INT;
token->text = expression;
}
private:
Lexer *mLexer;
const MacroSet *mMacroSet;
Diagnostics *mDiagnostics;
};
DirectiveParser::DirectiveParser(Tokenizer *tokenizer, DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
...@@ -839,7 +774,7 @@ void DirectiveParser::parseLine(Token *token) ...@@ -839,7 +774,7 @@ void DirectiveParser::parseLine(Token *token)
int line = 0, file = 0; int line = 0, file = 0;
int state = LINE_NUMBER; int state = LINE_NUMBER;
MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false);
macroExpander.lex(token); macroExpander.lex(token);
while ((token->type != '\n') && (token->type != Token::LAST)) while ((token->type != '\n') && (token->type != Token::LAST))
{ {
...@@ -954,8 +889,7 @@ int DirectiveParser::parseExpressionIf(Token *token) ...@@ -954,8 +889,7 @@ int DirectiveParser::parseExpressionIf(Token *token)
assert((getDirective(token) == DIRECTIVE_IF) || assert((getDirective(token) == DIRECTIVE_IF) ||
(getDirective(token) == DIRECTIVE_ELIF)); (getDirective(token) == DIRECTIVE_ELIF));
DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, true);
MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics);
ExpressionParser expressionParser(&macroExpander, mDiagnostics); ExpressionParser expressionParser(&macroExpander, mDiagnostics);
int expression = 0; int expression = 0;
......
...@@ -48,10 +48,9 @@ class TokenLexer : public Lexer ...@@ -48,10 +48,9 @@ class TokenLexer : public Lexer
MacroExpander::MacroExpander(Lexer *lexer, MacroExpander::MacroExpander(Lexer *lexer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics) Diagnostics *diagnostics,
: mLexer(lexer), bool parseDefined)
mMacroSet(macroSet), : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mParseDefined(parseDefined)
mDiagnostics(diagnostics)
{ {
} }
...@@ -67,11 +66,54 @@ void MacroExpander::lex(Token *token) ...@@ -67,11 +66,54 @@ void MacroExpander::lex(Token *token)
{ {
while (true) while (true)
{ {
const char kDefined[] = "defined";
getToken(token); getToken(token);
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
break; break;
// Defined operator is parsed here since it may be generated by macro expansion.
// Defined operator produced by macro expansion has undefined behavior according to C++
// spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this
// behavior is needed for passing dEQP tests, which enforce stricter compatibility between
// implementations.
if (mParseDefined && token->text == kDefined)
{
bool paren = false;
getToken(token);
if (token->type == '(')
{
paren = true;
getToken(token);
}
if (token->type != Token::IDENTIFIER)
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
token->text);
break;
}
auto iter = mMacroSet->find(token->text);
std::string expression = iter != mMacroSet->end() ? "1" : "0";
if (paren)
{
getToken(token);
if (token->type != ')')
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
token->text);
break;
}
}
// We have a valid defined operator.
// Convert the current token into a CONST_INT token.
token->type = Token::CONST_INT;
token->text = expression;
break;
}
if (token->expansionDisabled()) if (token->expansionDisabled())
break; break;
...@@ -325,7 +367,7 @@ bool MacroExpander::collectMacroArgs(const Macro &macro, ...@@ -325,7 +367,7 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
{ {
MacroArg &arg = args->at(i); MacroArg &arg = args->at(i);
TokenLexer lexer(&arg); TokenLexer lexer(&arg);
MacroExpander expander(&lexer, mMacroSet, mDiagnostics); MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mParseDefined);
arg.clear(); arg.clear();
expander.lex(&token); expander.lex(&token);
......
...@@ -24,7 +24,7 @@ struct SourceLocation; ...@@ -24,7 +24,7 @@ struct SourceLocation;
class MacroExpander : public Lexer class MacroExpander : public Lexer
{ {
public: public:
MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics); MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics, bool parseDefined);
~MacroExpander() override; ~MacroExpander() override;
void lex(Token *token) override; void lex(Token *token) override;
...@@ -81,6 +81,7 @@ class MacroExpander : public Lexer ...@@ -81,6 +81,7 @@ class MacroExpander : public Lexer
Lexer *mLexer; Lexer *mLexer;
MacroSet *mMacroSet; MacroSet *mMacroSet;
Diagnostics *mDiagnostics; Diagnostics *mDiagnostics;
bool mParseDefined;
std::auto_ptr<Token> mReserveToken; std::auto_ptr<Token> mReserveToken;
std::vector<MacroContext *> mContextStack; std::vector<MacroContext *> mContextStack;
......
...@@ -26,12 +26,11 @@ struct PreprocessorImpl ...@@ -26,12 +26,11 @@ struct PreprocessorImpl
DirectiveParser directiveParser; DirectiveParser directiveParser;
MacroExpander macroExpander; MacroExpander macroExpander;
PreprocessorImpl(Diagnostics *diag, PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler)
DirectiveHandler *directiveHandler)
: diagnostics(diag), : diagnostics(diag),
tokenizer(diag), tokenizer(diag),
directiveParser(&tokenizer, &macroSet, diag, directiveHandler), directiveParser(&tokenizer, &macroSet, diag, directiveHandler),
macroExpander(&directiveParser, &macroSet, diag) macroExpander(&directiveParser, &macroSet, diag, false)
{ {
} }
}; };
......
...@@ -892,3 +892,33 @@ TEST_F(DefineTest, Predefined_FILE2) ...@@ -892,3 +892,33 @@ TEST_F(DefineTest, Predefined_FILE2)
EXPECT_EQ(pp::Token::CONST_INT, token.type); EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("21", token.text); EXPECT_EQ("21", token.text);
} }
// Defined operator produced by macro expansion should be parsed inside #if directives
TEST_F(DefineTest, ExpandedDefinedParsedInsideIf)
{
const char *input =
"#define bar 1\n"
"#define foo defined(bar)\n"
"#if foo\n"
"bar\n"
"#endif\n";
const char *expected =
"\n"
"\n"
"\n"
"1\n"
"\n";
preprocess(input, expected);
}
// Defined operator produced by macro expansion should not be parsed outside #if directives
TEST_F(DefineTest, ExpandedDefinedNotParsedOutsideIf)
{
const char *input =
"#define foo defined(bar)\n"
"foo\n";
const char *expected =
"\n"
"defined(bar)\n";
preprocess(input, expected);
}
...@@ -891,3 +891,64 @@ TEST_F(IfTest, ShortCircuitedUndefined) ...@@ -891,3 +891,64 @@ TEST_F(IfTest, ShortCircuitedUndefined)
preprocess(str, expected); preprocess(str, expected);
} }
// Defined operator produced by macro expansion has undefined behavior according to C++ spec,
// which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is
// needed for passing dEQP tests, which enforce stricter compatibility between implementations.
TEST_F(IfTest, DefinedOperatorValidAfterMacroExpansion)
{
const char *str =
"#define foo defined\n"
"#if !foo bar\n"
"pass\n"
"#endif\n";
const char *expected =
"\n"
"\n"
"pass\n"
"\n";
preprocess(str, expected);
}
// Unterminated defined operator generated by macro expansion should generate appropriate errors.
// Defined operator produced by macro expansion has undefined behavior according to C++ spec,
// which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is
// needed for passing dEQP tests, which enforce stricter compatibility between implementations.
TEST_F(IfTest, UnterminatedDefinedInMacro)
{
const char *str =
"#define foo defined(\n"
"#if foo\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_UNEXPECTED_TOKEN, pp::SourceLocation(0, 2), "\n"));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 2), "syntax error"));
pp::Token token;
mPreprocessor.lex(&token);
}
// Unterminated defined operator generated by macro expansion should generate appropriate errors.
// Defined operator produced by macro expansion has undefined behavior according to C++ spec,
// which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is
// needed for passing dEQP tests, which enforce stricter compatibility between implementations.
TEST_F(IfTest, UnterminatedDefinedInMacro2)
{
const char *str =
"#define foo defined(bar\n"
"#if foo\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_UNEXPECTED_TOKEN, pp::SourceLocation(0, 2), "\n"));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 2), "syntax error"));
pp::Token token;
mPreprocessor.lex(&token);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment