Skip to content

Commit 7ef5b93

Browse files
committed
Handle nested type deductions
1 parent 73fd65a commit 7ef5b93

2 files changed

Lines changed: 56 additions & 7 deletions

File tree

‎lib/templatesimplifier.cpp‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,20 +1293,20 @@ std::string TemplateSimplifier::deduceFunctionTemplateArguments(
12931293
if (tok->function() && !tok->function()->templateDef)
12941294
return qualification;
12951295

1296-
if (!mUseTypeInformation) {
1297-
// Can the deduction succeed later, when type information is available? Scope
1298-
// matching is not possible yet - the declaration may be in a base class - so
1299-
// consider all declarations with this name.
1296+
// Without type information for this call the deduction can not be done now: either
1297+
// the type information pass has not run yet, or the call is in a function body that
1298+
// an instantiation created during this pass - the symbol database does not know the
1299+
// new tokens yet. Can the deduction succeed later, when type information is
1300+
// available? Scope matching is not possible yet - the declaration may be in a base
1301+
// class - so consider all declarations with this name.
1302+
if (!mUseTypeInformation || !tok->scope()) {
13001303
if (std::any_of(range.first, range.second, [&](const std::pair<const std::string, const TokenAndName*>& entry) {
13011304
return deductionCache->supportedParameterCount(*entry.second) == instantiationArgs.size();
13021305
}))
13031306
mPendingTypeDeductions = true;
13041307
return qualification;
13051308
}
13061309

1307-
if (!tok->scope())
1308-
return qualification;
1309-
13101310
// the scopes where the call name is looked up - see CallLookupLevels
13111311
CallLookupLevels lookup;
13121312
if (!qualification.empty())

‎test/testsimplifytemplate.cpp‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ class TestSimplifyTemplate : public TestFixture {
315315
TEST_CASE(templateTypeDeductionRedeclared); // twice declared template: the surviving declaration stays valid
316316
TEST_CASE(templateTypeDeductionFullRebuild); // --template-full-rebuild gives the same result
317317
TEST_CASE(templateTypeDeductionMultiPass); // a pending deduction survives later simplifier passes
318+
TEST_CASE(templateTypeDeductionNested); // calls inside a body created by deduction are deduced too
318319
TEST_CASE(templateTypeDeductionEnum); // enum arguments deduce the enum, not its underlying type
319320
TEST_CASE(templateTypeDeductionArray); // array arguments: only the decayed dimension is a pointer
320321
TEST_CASE(templateTypeDeductionConstPointer); // "const T *": the parameter provides the pointee const
@@ -7275,6 +7276,54 @@ class TestSimplifyTemplate : public TestFixture {
72757276
ASSERT_EQUALS("", errout_str());
72767277
}
72777278

7279+
void templateTypeDeductionNested()
7280+
{ // a call inside a function body that a deduction instantiated has no symbol
7281+
// database scope yet when it is seen - the deduction must be retried in the next
7282+
// round instead of being given up
7283+
{
7284+
const char code[] = "template<class T> T twice(T t) { return t * 2; }\n"
7285+
"template<class T> T thrice(T t) { return twice(t) + t; }\n"
7286+
"int g(int a) { return thrice(a); }";
7287+
const char exp[] = "int twice<int> ( int t ) ; "
7288+
"int thrice<int> ( int t ) ; "
7289+
"int g ( int a ) { return thrice<int> ( a ) ; } "
7290+
"int thrice<int> ( int t ) { return twice<int> ( t ) + t ; } "
7291+
"int twice<int> ( int t ) { return t * 2 ; }";
7292+
ASSERT_EQUALS(exp, tok(code));
7293+
ASSERT_EQUALS(exp, tok(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7294+
ASSERT_EQUALS("", errout_str());
7295+
}
7296+
{ // three levels, each one needs another round
7297+
const char code[] = "template<class T> T a(T t) { return t; }\n"
7298+
"template<class T> T b(T t) { return a(t); }\n"
7299+
"template<class T> T c(T t) { return b(t); }\n"
7300+
"long g(long x) { return c(x); }";
7301+
const char exp[] = "long a<long> ( long t ) ; "
7302+
"long b<long> ( long t ) ; "
7303+
"long c<long> ( long t ) ; "
7304+
"long g ( long x ) { return c<long> ( x ) ; } "
7305+
"long c<long> ( long t ) { return b<long> ( t ) ; } "
7306+
"long b<long> ( long t ) { return a<long> ( t ) ; } "
7307+
"long a<long> ( long t ) { return t ; }";
7308+
ASSERT_EQUALS(exp, tok(code));
7309+
ASSERT_EQUALS(exp, tok(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7310+
ASSERT_EQUALS("", errout_str());
7311+
}
7312+
{ // the nested call uses a different type than the outer one
7313+
const char code[] = "template<class T> void sink(T t) { (void)t; }\n"
7314+
"template<class T> void outer(T t) { sink(&t); }\n"
7315+
"void g(int x) { outer(x); }";
7316+
const char exp[] = "void sink<int*> ( int * t ) ; "
7317+
"void outer<int> ( int t ) ; "
7318+
"void g ( int x ) { outer<int> ( x ) ; } "
7319+
"void outer<int> ( int t ) { sink<int*> ( & t ) ; } "
7320+
"void sink<int*> ( int * t ) { ( void ) t ; }";
7321+
ASSERT_EQUALS(exp, tok(code));
7322+
ASSERT_EQUALS(exp, tok(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7323+
ASSERT_EQUALS("", errout_str());
7324+
}
7325+
}
7326+
72787327
void templateTypeDeductionEnum()
72797328
{
72807329
{ // an enum variable has the ValueType of the underlying integer type - the enum

0 commit comments

Comments
 (0)