From 10fcf731d9af73a9ae5ace49b3fcae4d00908d12 Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Mon, 25 Mar 2019 09:56:51 -0400 Subject: [PATCH] Fixed #9021 (template simplifier: crash in simplifyCalculations) (#1757) --- lib/templatesimplifier.cpp | 144 +++++++++++++++++++++++++--------- test/testsimplifytemplate.cpp | 48 +++++++++++- 2 files changed, 154 insertions(+), 38 deletions(-) diff --git a/lib/templatesimplifier.cpp b/lib/templatesimplifier.cpp index 43e749027..481d74e8d 100644 --- a/lib/templatesimplifier.cpp +++ b/lib/templatesimplifier.cpp @@ -354,6 +354,13 @@ unsigned int TemplateSimplifier::templateParameters(const Token *tok) tok = tok->link(); if (tok) tok = tok->next(); + if (tok->str() == ">" && level == 0) + return numberOfParameters; + else if (tok->str() == "," && level == 0) { + ++numberOfParameters; + tok = tok->next(); + } + continue; } // skip std:: @@ -1930,12 +1937,14 @@ static Token *skipTernaryOp(Token *tok, Token *backToken) void TemplateSimplifier::simplifyTemplateArgs(Token *start, Token *end) { + // start could be erased so use the token before start if available + Token * first = (start && start->previous()) ? start->previous() : mTokenList.front(); bool again = true; while (again) { again = false; - for (Token *tok = start; tok && tok != end; tok = tok->next()) { + for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (tok->str() == "sizeof") { // sizeof('x') if (Token::Match(tok->next(), "( %char% )")) { @@ -1980,11 +1989,11 @@ void TemplateSimplifier::simplifyTemplateArgs(Token *start, Token *end) } } - if (simplifyCalculations(start, end)) + if (simplifyCalculations(first->next(), end)) again = true; - for (Token *tok = start; tok && tok != end; tok = tok->next()) { - if (tok->str() == "?" && tok->previous()->isNumber()) { + for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { + if (tok->str() == "?" && (tok->previous()->isNumber() || tok->previous()->isBoolean())) { const int offset = (tok->previous()->str() == ")") ? 2 : 1; // Find the token ":" then go to the next token @@ -2043,24 +2052,60 @@ void TemplateSimplifier::simplifyTemplateArgs(Token *start, Token *end) } } - for (Token *tok = start; tok && tok != end; tok = tok->next()) { - if (Token::Match(tok, "( %num% )") && !Token::Match(tok->previous(), "%name%")) { + for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { + if (Token::Match(tok, "( %num%|%bool% )") && + (tok->previous() && !Token::Match(tok->previous(), "%name%"))) { tok->deleteThis(); tok->deleteNext(); + again = true; } } } } +static bool validTokenStart(bool bounded, const Token *tok, const Token *frontToken, int offset) +{ + if (!bounded) + return true; + + if (frontToken) + frontToken = frontToken->previous(); + + while (tok && offset <= 0) { + if (tok == frontToken) + return false; + ++offset; + tok = tok->previous(); + } + + return tok && offset > 0; +} + +static bool validTokenEnd(bool bounded, const Token *tok, const Token *backToken, int offset) +{ + if (!bounded) + return true; + + while (tok && offset >= 0) { + if (tok == backToken) + return false; + --offset; + tok = tok->next(); + } + + return tok && offset < 0; +} + // TODO: This is not the correct class for simplifyCalculations(), so it // should be moved away. bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToken) { bool ret = false; + const bool bounded = frontToken || backToken; if (!frontToken) { frontToken = mTokenList.front(); } - for (Token *tok = frontToken; tok != backToken; tok = tok->next()) { + for (Token *tok = frontToken; tok && tok != backToken; tok = tok->next()) { // Remove parentheses around variable.. // keep parentheses here: dynamic_cast(p); // keep parentheses here: A operator * (int); @@ -2069,8 +2114,11 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke // keep parentheses here: operator new [] (size_t); // keep parentheses here: Functor()(a ... ) // keep parentheses here: ) ( var ) ; - if ((Token::Match(tok->next(), "( %name% ) ;|)|,|]") || - (Token::Match(tok->next(), "( %name% ) %cop%") && (tok->tokAt(2)->varId()>0 || !Token::Match(tok->tokAt(4), "[*&+-~]")))) && + if (validTokenEnd(bounded, tok, backToken, 4) && + (Token::Match(tok->next(), "( %name% ) ;|)|,|]") || + (Token::Match(tok->next(), "( %name% ) %cop%") && + (tok->tokAt(2)->varId()>0 || + !Token::Match(tok->tokAt(4), "[*&+-~]")))) && !tok->isName() && tok->str() != ">" && tok->str() != ")" && @@ -2081,23 +2129,28 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke ret = true; } - if (Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { + if (validTokenEnd(bounded, tok, backToken, 3) && + Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { tok->str(MathLib::toString(MathLib::toLongNumber(tok->str()))); } - if (tok->isNumber()) { - if (simplifyNumericCalculations(tok)) { + if (tok && tok->isNumber()) { + if (validTokenEnd(bounded, tok, backToken, 2) && + simplifyNumericCalculations(tok)) { ret = true; Token *prev = tok->tokAt(-2); - while (prev && simplifyNumericCalculations(prev)) { + while (validTokenStart(bounded, tok, frontToken, -2) && + prev && simplifyNumericCalculations(prev)) { tok = prev; prev = prev->tokAt(-2); } } // Remove redundant conditions (0&&x) (1||x) - if (Token::Match(tok->previous(), "[(=,] 0 &&") || - Token::Match(tok->previous(), "[(=,] 1 %oror%")) { + if (validTokenStart(bounded, tok, frontToken, -1) && + validTokenEnd(bounded, tok, backToken, 1) && + (Token::Match(tok->previous(), "[(=,] 0 &&") || + Token::Match(tok->previous(), "[(=,] 1 %oror%"))) { unsigned int par = 0; const Token *tok2 = tok; const bool andAnd = (tok->next()->str() == "&&"); @@ -2118,9 +2171,10 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke continue; } - if (tok->str() == "0") { - if ((Token::Match(tok->previous(), "[+-] 0 %cop%|;") && isLowerThanMulDiv(tok->next())) || - (Token::Match(tok->previous(), "%or% 0 %cop%|;") && isLowerThanXor(tok->next()))) { + if (tok->str() == "0" && validTokenStart(bounded, tok, frontToken, -1)) { + if (validTokenEnd(bounded, tok, backToken, 1) && + ((Token::Match(tok->previous(), "[+-] 0 %cop%|;") && isLowerThanMulDiv(tok->next())) || + (Token::Match(tok->previous(), "%or% 0 %cop%|;") && isLowerThanXor(tok->next())))) { tok = tok->previous(); if (Token::Match(tok->tokAt(-4), "[;{}] %name% = %name% [+-|] 0 ;") && tok->strAt(-3) == tok->previous()->str()) { @@ -2131,22 +2185,26 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke tok->deleteNext(2); } ret = true; - } else if (Token::Match(tok->previous(), "[=([,] 0 [+|]") || - Token::Match(tok->previous(), "return|case 0 [+|]")) { + } else if (validTokenEnd(bounded, tok, backToken, 1) && + (Token::Match(tok->previous(), "[=([,] 0 [+|]") || + Token::Match(tok->previous(), "return|case 0 [+|]"))) { tok = tok->previous(); tok->deleteNext(2); ret = true; - } else if (Token::Match(tok->previous(), "[=[(,] 0 * %name%|%num% ,|]|)|;|=|%cop%") || - Token::Match(tok->previous(), "[=[(,] 0 * (") || - Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%") || - Token::Match(tok->previous(), "return|case 0 *|&& (")) { + } else if ((((Token::Match(tok->previous(), "[=[(,] 0 * %name%|%num% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%")) && + validTokenEnd(bounded, tok, backToken, 3)) || + (((Token::Match(tok->previous(), "[=[(,] 0 * (") || + Token::Match(tok->previous(), "return|case 0 *|&& (")) && + validTokenEnd(bounded, tok, backToken, 2))))) { tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; - } else if (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") || - Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%")) { + } else if (validTokenEnd(bounded, tok, backToken, 4) && + (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") @@ -2156,16 +2214,18 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke } } - if (tok->str() == "1") { - if (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") || - Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%")) { + if (tok->str() == "1" && validTokenStart(bounded, tok, frontToken, -1)) { + if (validTokenEnd(bounded, tok, backToken, 3) && + (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; - } else if (Token::Match(tok->previous(), "[=[(,] 1 %oror% *|& %any% ,|]|)|;|=|%cop%") || - Token::Match(tok->previous(), "return|case 1 %oror% *|& %any% ,|:|;|=|%cop%")) { + } else if (validTokenEnd(bounded, tok, backToken, 4) && + (Token::Match(tok->previous(), "[=[(,] 1 %oror% *|& %any% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 1 %oror% *|& %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") @@ -2175,7 +2235,10 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke } } - if (Token::Match(tok->tokAt(-2), "%any% * 1") || Token::Match(tok->previous(), "%any% 1 *")) { + if ((Token::Match(tok->tokAt(-2), "%any% * 1") && + validTokenStart(bounded, tok, frontToken, -2)) || + (Token::Match(tok->previous(), "%any% 1 *") && + validTokenStart(bounded, tok, frontToken, -1))) { tok = tok->previous(); if (tok->str() == "*") tok = tok->previous(); @@ -2184,15 +2247,19 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke } // Remove parentheses around number.. - if (Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && tok->strAt(-2) != ">") { + if (validTokenStart(bounded, tok, frontToken, -2) && + Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && + tok->strAt(-2) != ">") { tok = tok->previous(); tok->deleteThis(); tok->deleteNext(); ret = true; } - if (Token::Match(tok->previous(), "( 0 [|+]") || - Token::Match(tok->previous(), "[|+-] 0 )")) { + if (validTokenStart(bounded, tok, frontToken, -1) && + validTokenEnd(bounded, tok, backToken, 1) && + (Token::Match(tok->previous(), "( 0 [|+]") || + Token::Match(tok->previous(), "[|+-] 0 )"))) { tok = tok->previous(); if (Token::Match(tok, "[|+-]")) tok = tok->previous(); @@ -2200,10 +2267,13 @@ bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToke ret = true; } - if (Token::Match(tok, "%num% %comp% %num%") && + if (validTokenEnd(bounded, tok, backToken, 2) && + Token::Match(tok, "%num% %comp% %num%") && MathLib::isInt(tok->str()) && MathLib::isInt(tok->strAt(2))) { - if (Token::Match(tok->previous(), "(|&&|%oror%") && Token::Match(tok->tokAt(3), ")|&&|%oror%|?")) { + if (validTokenStart(bounded, tok, frontToken, -1) && + Token::Match(tok->previous(), "(|&&|%oror%") && + Token::Match(tok->tokAt(3), ")|&&|%oror%|?")) { const MathLib::bigint op1(MathLib::toLongNumber(tok->str())); const std::string &cmp(tok->next()->str()); const MathLib::bigint op2(MathLib::toLongNumber(tok->strAt(2))); diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index b86a53602..ad02bf6be 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -141,6 +141,7 @@ private: TEST_CASE(template101); // #8968 TEST_CASE(template102); // #9005 TEST_CASE(template103); + TEST_CASE(template104); // #9021 TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) @@ -184,6 +185,8 @@ private: TEST_CASE(templateTypeDeduction1); // #8962 TEST_CASE(templateTypeDeduction2); + + TEST_CASE(simplifyTemplateArgs); } std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { @@ -1275,7 +1278,7 @@ private: "void foo ( ) { " "Foo myFoo ; " "} struct Foo { " - "std :: array < int , true ? 1 : 2 > mfoo ; " + "std :: array < int , 1 > mfoo ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); @@ -2354,6 +2357,32 @@ private: ASSERT_EQUALS(exp, tok(code)); } + void template104() { // #9021 + const char code[] = "template < int i >\n" + "auto key ( ) { return hana :: test :: ct_eq < i > { } ; }\n" + "template < int i >\n" + "auto val ( ) { return hana :: test :: ct_eq < - i > { } ; }\n" + "template < int i , int j >\n" + "auto p ( ) { return :: minimal_product ( key < i > ( ) , val < j > ( ) ) ; }\n" + "int main ( ) {\n" + " BOOST_HANA_CONSTANT_CHECK ( hana :: equal (\n" + " hana :: at_key ( hana :: make_map ( p < 0 , 0 > ( ) ) , key < 0 > ( ) ) ,\n" + " val < 0 > ( ) ) ) ;\n" + "}"; + const char exp[] = "auto key<0> ( ) ; " + "auto val<0> ( ) ; " + "auto p<0,0> ( ) ; " + "int main ( ) { " + "BOOST_HANA_CONSTANT_CHECK ( hana :: equal ( " + "hana :: at_key ( hana :: make_map ( p<0,0> ( ) ) , key<0> ( ) ) , " + "val<0> ( ) ) ) ; " + "} " + "auto p<0,0> ( ) { return :: minimal_product ( key<0> ( ) , val<0> ( ) ) ; } " + "auto val<0> ( ) { return hana :: test :: ct_eq < - 0 > { } ; } " + "auto key<0> ( ) { return hana :: test :: ct_eq < 0 > { } ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + void template_specialization_1() { // #7868 - template specialization template struct S> {..}; const char code[] = "template struct C {};\n" "template struct S {a};\n" @@ -3435,6 +3464,23 @@ private: TODO_ASSERT_EQUALS(expected, actual, tok(code)); } + void simplifyTemplateArgs() { + ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template foo = N; foo < ( 2 ) >;")); + ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template foo = N; foo < 1 + 1 >;")); + ASSERT_EQUALS("foo<2> = 2 ; foo<2> ;", tok("template foo = N; foo < ( 1 + 1 ) >;")); + + ASSERT_EQUALS("foo<2,2> = 4 ; foo<2,2> ;", tok("template foo = N * M; foo < ( 2 ), ( 2 ) >;")); + ASSERT_EQUALS("foo<2,2> = 4 ; foo<2,2> ;", tok("template foo = N * M; foo < 1 + 1, 1 + 1 >;")); + ASSERT_EQUALS("foo<2,2> = 4 ; foo<2,2> ;", tok("template foo = N * M; foo < ( 1 + 1 ), ( 1 + 1 ) >;")); + + ASSERT_EQUALS("foo = true ; foo ;", tok("template foo = N; foo < true ? true : false >;")); + ASSERT_EQUALS("foo = false ; foo ;", tok("template foo = N; foo < false ? true : false >;")); + ASSERT_EQUALS("foo = true ; foo ;", tok("template foo = N; foo < 1 ? true : false >;")); + ASSERT_EQUALS("foo = false ; foo ;", tok("template foo = N; foo < 0 ? true : false >;")); + ASSERT_EQUALS("foo = true ; foo ;", tok("template foo = N; foo < (1 + 1 ) ? true : false >;")); + ASSERT_EQUALS("foo = false ; foo ;", tok("template foo = N; foo < ( 1 - 1) ? true : false >;")); + } + }; REGISTER_TEST(TestSimplifyTemplate)