Skip to content

Commit 127f7a1

Browse files
committed
Handle shadowing
1 parent 71c2a23 commit 127f7a1

2 files changed

Lines changed: 140 additions & 2 deletions

File tree

‎lib/templatesimplifier.cpp‎

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,15 +810,18 @@ struct ParameterShape {
810810
struct DeducedType {
811811
std::string typeStr; // base type, e.g. "int" or "MyClass"
812812
std::string qualification; // enclosing scopes of record and enum types, "N :: A"
813+
const Scope* typeScope = nullptr; // the scope of a record or enum type
814+
bool globalQualification = false; // write the type with a leading "::"
813815
unsigned int constness = 0; // bit 0 = data const, bit 1 = first '*' const, ..
814816
unsigned int pointer = 0; // number of '*'
815817
bool isUnsigned = false;
816818
bool isLong = false;
817819

818820
bool operator==(const DeducedType& other) const
819821
{
820-
return typeStr == other.typeStr && qualification == other.qualification && constness == other.constness &&
821-
pointer == other.pointer && isUnsigned == other.isUnsigned && isLong == other.isLong;
822+
return typeStr == other.typeStr && qualification == other.qualification && typeScope == other.typeScope &&
823+
constness == other.constness && pointer == other.pointer && isUnsigned == other.isUnsigned &&
824+
isLong == other.isLong;
822825
}
823826
};
824827

@@ -916,9 +919,40 @@ static DeducedType deducedScopeType(DeducedType deduced, const Scope* typeScope)
916919
if (deduced.qualification.empty() && enclosing && enclosing->type != ScopeType::eGlobal)
917920
return DeducedType(); // local types etc. cannot be named - bail out
918921
deduced.typeStr = typeScope->className;
922+
deduced.typeScope = typeScope;
919923
return deduced;
920924
}
921925

926+
// Does the name of the deduced type, looked up from the given scope, find another scope
927+
// than the one the type is in? The instantiation is declared in the scope of the
928+
// template, so "S" deduced from "::S" would be resolved to "X::S" when the template is
929+
// in namespace X and X has its own S. Such a type must be written with a leading "::".
930+
static bool deducedTypeIsShadowed(const Scope* from, const DeducedType& deduced)
931+
{
932+
if (!deduced.typeScope)
933+
return false;
934+
// the outermost named scope of the type: "N" for "N :: A :: M", the type itself for "S"
935+
const Scope* outermost = deduced.typeScope;
936+
while (outermost->nestedIn && outermost->nestedIn->type != ScopeType::eGlobal)
937+
outermost = outermost->nestedIn;
938+
const std::string& name = outermost->className;
939+
for (const Scope* scope = from; scope && scope->type != ScopeType::eGlobal; scope = scope->nestedIn) {
940+
// in a class the nested types of the base classes are visible too
941+
std::vector<const Scope*> lookupScopes;
942+
if (scope->isClassOrStructOrUnion())
943+
lookupScopes = scope->findAssociatedScopes();
944+
else
945+
lookupScopes.push_back(scope);
946+
for (const Scope* lookupScope : lookupScopes) {
947+
for (const Scope* nested : lookupScope->nestedList) {
948+
if (nested->className == name)
949+
return nested != outermost;
950+
}
951+
}
952+
}
953+
return false;
954+
}
955+
922956
// Convert the type of an argument expression to the type that the parameter shape
923957
// deduces for its template parameter. The returned type has an empty typeStr when the
924958
// type is not supported.
@@ -1070,6 +1104,8 @@ static void insertDeducedType(Token* tok, const DeducedType& deduced)
10701104
{
10711105
if (deduced.constness & 1U)
10721106
tok = tok->insertToken("const");
1107+
if (deduced.globalQualification)
1108+
tok = tok->insertToken("::");
10731109
const std::string& qualification = deduced.qualification;
10741110
for (std::string::size_type pos = 0; pos < qualification.size();) {
10751111
std::string::size_type end = qualification.find(' ', pos);
@@ -1335,6 +1371,8 @@ std::string TemplateSimplifier::deduceFunctionTemplateArguments(
13351371
// find the function template declarations this call could instantiate
13361372
std::vector<const TokenAndName*> candidates;
13371373
std::string baseClassQualification;
1374+
// the scope the template is declared in - the instantiation is declared there too
1375+
const Scope* declarationScope = nullptr;
13381376
for (const LookupLevel& level : lookup.levels) {
13391377
// a non template function with the same name might be a better overload match
13401378
if (level.scope && scopeHasNonTemplateFunction(level.scope, tok->str()))
@@ -1347,6 +1385,7 @@ std::string TemplateSimplifier::deduceFunctionTemplateArguments(
13471385
if (!candidates.empty()) {
13481386
if (level.isBaseClass)
13491387
baseClassQualification = level.scopeName;
1388+
declarationScope = level.scope;
13501389
break;
13511390
}
13521391
}
@@ -1398,6 +1437,19 @@ std::string TemplateSimplifier::deduceFunctionTemplateArguments(
13981437
stored = std::move(deduced);
13991438
}
14001439

1440+
// a deduced type whose name is shadowed in the scope of the template must be written
1441+
// with a leading "::" so the instantiation does not use the wrong type
1442+
if (!declarationScope)
1443+
declarationScope = declaration->nameToken()->scope(); // explicitly qualified call
1444+
for (DeducedType& deduced : deducedTypes) {
1445+
if (!deduced.typeScope)
1446+
continue;
1447+
if (!declarationScope)
1448+
return qualification; // cannot tell which type the name would resolve to
1449+
if (deducedTypeIsShadowed(declarationScope, deduced))
1450+
deduced.globalQualification = true;
1451+
}
1452+
14011453
// when the declaration is in a base class, qualify the call site explicitly so the
14021454
// instantiation is matched with that declaration: "btf ( x )" => "Base :: btf ( x )"
14031455
if (!baseClassQualification.empty()) {

‎test/testsimplifytemplate.cpp‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ class TestSimplifyTemplate : public TestFixture {
317317
TEST_CASE(templateTypeDeductionMultiPass); // a pending deduction survives later simplifier passes
318318
TEST_CASE(templateTypeDeductionNested); // calls inside a body created by deduction are deduced too
319319
TEST_CASE(templateTypeDeductionOutOfLine); // member template defined out of line, class in a namespace
320+
TEST_CASE(templateTypeDeductionShadowedType); // the deduced type name means another type in the template's scope
320321
TEST_CASE(templateTypeDeductionEnum); // enum arguments deduce the enum, not its underlying type
321322
TEST_CASE(templateTypeDeductionArray); // array arguments: only the decayed dimension is a pointer
322323
TEST_CASE(templateTypeDeductionConstPointer); // "const T *": the parameter provides the pointee const
@@ -7421,6 +7422,91 @@ class TestSimplifyTemplate : public TestFixture {
74217422
}
74227423
}
74237424

7425+
void templateTypeDeductionShadowedType()
7426+
{ // the instantiation is declared in the scope of the template: when the name of
7427+
// the deduced type means another type there, the type must be written with a
7428+
// leading "::", otherwise the instantiation uses the wrong type
7429+
{ // a global type shadowed by a type in the namespace of the template
7430+
const char code[] = "struct S { int a[10]; };\n"
7431+
"namespace X {\n"
7432+
"struct S { int a[2]; };\n"
7433+
"template<class T> int f(const T& t) { return t.a[5]; }\n"
7434+
"int g(const ::S& s) { return f(s); }\n"
7435+
"}";
7436+
const char exp[] = "struct S { int a [ 10 ] ; } ; "
7437+
"namespace X { "
7438+
"struct S { int a [ 2 ] ; } ; "
7439+
"int f<::S> ( const :: S & t ) ; "
7440+
"int g ( const :: S & s ) { return f<::S> ( s ) ; } "
7441+
"} "
7442+
"int X :: f<::S> ( const :: S & t ) { return t . a [ 5 ] ; }";
7443+
ASSERT_EQUALS(exp, tok(code));
7444+
ASSERT_EQUALS(exp, tok(code, dinit(CheckOptions, $.templateFullRebuild = true)));
7445+
}
7446+
{ // a partly qualified name: the first scope name is shadowed
7447+
const char code[] = "namespace N { struct S { int a[10]; }; }\n"
7448+
"namespace X {\n"
7449+
"namespace N { struct S { int a[2]; }; }\n"
7450+
"template<class T> int f(const T& t) { return t.a[5]; }\n"
7451+
"int g(const ::N::S& s) { return f(s); }\n"
7452+
"}";
7453+
const char exp[] = "namespace N { struct S { int a [ 10 ] ; } ; } "
7454+
"namespace X { "
7455+
"namespace N { struct S { int a [ 2 ] ; } ; } "
7456+
"int f<::N::S> ( const :: N :: S & t ) ; "
7457+
"int g ( const :: N :: S & s ) { return f<::N::S> ( s ) ; } "
7458+
"} "
7459+
"int X :: f<::N::S> ( const :: N :: S & t ) { return t . a [ 5 ] ; }";
7460+
ASSERT_EQUALS(exp, tok(code));
7461+
}
7462+
{ // an explicitly qualified call from outside the namespace
7463+
const char code[] = "struct S { int a[10]; };\n"
7464+
"namespace X {\n"
7465+
"struct S { int a[2]; };\n"
7466+
"template<class T> int f(const T& t) { return t.a[5]; }\n"
7467+
"}\n"
7468+
"int g(const S& s) { return X::f(s); }";
7469+
const char exp[] = "struct S { int a [ 10 ] ; } ; "
7470+
"namespace X { "
7471+
"struct S { int a [ 2 ] ; } ; "
7472+
"int f<::S> ( const :: S & t ) ; "
7473+
"} "
7474+
"int g ( const S & s ) { return X :: f<::S> ( s ) ; } "
7475+
"int X :: f<::S> ( const :: S & t ) { return t . a [ 5 ] ; }";
7476+
ASSERT_EQUALS(exp, tok(code));
7477+
}
7478+
{ // the type in the namespace itself is not shadowed: no leading "::"
7479+
const char code[] = "namespace X {\n"
7480+
"struct S { int a[10]; };\n"
7481+
"template<class T> int f(const T& t) { return t.a[5]; }\n"
7482+
"int g(const S& s) { return f(s); }\n"
7483+
"}";
7484+
const char exp[] = "namespace X { "
7485+
"struct S { int a [ 10 ] ; } ; "
7486+
"int f<X::S> ( const X :: S & t ) ; "
7487+
"int g ( const S & s ) { return f<X::S> ( s ) ; } "
7488+
"} "
7489+
"int X :: f<X::S> ( const X :: S & t ) { return t . a [ 5 ] ; }";
7490+
ASSERT_EQUALS(exp, tok(code));
7491+
}
7492+
{ // a member template: nested types of the class and of its base classes shadow
7493+
const char code[] = "struct S { int a[10]; };\n"
7494+
"struct Base { struct S { int a[2]; }; };\n"
7495+
"struct C : Base {\n"
7496+
" template<class T> int f(const T& t) { return t.a[5]; }\n"
7497+
" int g(const ::S& s) { return f(s); }\n"
7498+
"};";
7499+
const char exp[] = "struct S { int a [ 10 ] ; } ; "
7500+
"struct Base { struct S { int a [ 2 ] ; } ; } ; "
7501+
"struct C : Base { "
7502+
"int f<::S> ( const :: S & t ) ; "
7503+
"int g ( const :: S & s ) { return f<::S> ( s ) ; } "
7504+
"} ; "
7505+
"int C :: f<::S> ( const :: S & t ) { return t . a [ 5 ] ; }";
7506+
ASSERT_EQUALS(exp, tok(code));
7507+
}
7508+
}
7509+
74247510
void templateTypeDeductionEnum()
74257511
{
74267512
{ // an enum variable has the ValueType of the underlying integer type - the enum

0 commit comments

Comments
 (0)