@@ -316,6 +316,7 @@ class TestSimplifyTemplate : public TestFixture {
316316 TEST_CASE (templateTypeDeductionFullRebuild); // --template-full-rebuild gives the same result
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
319+ TEST_CASE (templateTypeDeductionOutOfLine); // member template defined out of line, class in a namespace
319320 TEST_CASE (templateTypeDeductionEnum); // enum arguments deduce the enum, not its underlying type
320321 TEST_CASE (templateTypeDeductionArray); // array arguments: only the decayed dimension is a pointer
321322 TEST_CASE (templateTypeDeductionConstPointer); // "const T *": the parameter provides the pointee const
@@ -7324,6 +7325,102 @@ class TestSimplifyTemplate : public TestFixture {
73247325 }
73257326 }
73267327
7328+ void templateTypeDeductionOutOfLine ()
7329+ { // the out of line definition is already qualified with the class and the
7330+ // namespace - the instantiation must not get the qualification a second time,
7331+ // otherwise the body is not a member function body and the members used in it
7332+ // get no variable id
7333+ {
7334+ const char code[] = " namespace N {\n "
7335+ " struct C {\n "
7336+ " template<class T> T m(T t);\n "
7337+ " int x;\n "
7338+ " int use(int i) { return m(i); }\n "
7339+ " };\n "
7340+ " }\n "
7341+ " template<class T> T N::C::m(T t) { return t + x; }" ;
7342+ // the incremental setVarId() gives the new tokens ids above the existing ones
7343+ const char exp[] = " namespace N { "
7344+ " struct C { "
7345+ " int m<int> ( int t@5 ) ; "
7346+ " int x@2 ; "
7347+ " int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7348+ " } ; "
7349+ " } "
7350+ " int N :: C :: m<int> ( int t@6 ) { return t@6 + x@2 ; }" ;
7351+ ASSERT_EQUALS (exp, tokVarId (code));
7352+ const char expFullRebuild[] = " namespace N { "
7353+ " struct C { "
7354+ " int m<int> ( int t@1 ) ; "
7355+ " int x@2 ; "
7356+ " int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7357+ " } ; "
7358+ " } "
7359+ " int N :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }" ;
7360+ ASSERT_EQUALS (expFullRebuild, tokVarId (code, dinit (CheckOptions, $.templateFullRebuild = true )));
7361+ }
7362+ { // explicit template arguments: same expansion
7363+ const char code[] = " namespace N {\n "
7364+ " struct C {\n "
7365+ " template<class T> T m(T t);\n "
7366+ " int x;\n "
7367+ " int use(int i) { return m<int>(i); }\n "
7368+ " };\n "
7369+ " }\n "
7370+ " template<class T> T N::C::m(T t) { return t + x; }" ;
7371+ const char exp[] = " namespace N { "
7372+ " struct C { "
7373+ " int m<int> ( int t@1 ) ; "
7374+ " int x@2 ; "
7375+ " int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7376+ " } ; "
7377+ " } "
7378+ " int N :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }" ;
7379+ ASSERT_EQUALS (exp, tokVarId (code));
7380+ }
7381+ { // the definition is qualified with the class only: the namespace is added
7382+ // (the instantiation is appended after the namespace, like all instantiations)
7383+ const char code[] = " namespace N {\n "
7384+ " struct C {\n "
7385+ " template<class T> T m(T t);\n "
7386+ " int x;\n "
7387+ " int use(int i) { return m(i); }\n "
7388+ " };\n "
7389+ " template<class T> T C::m(T t) { return t + x; }\n "
7390+ " }" ;
7391+ const char exp[] = " namespace N { "
7392+ " struct C { "
7393+ " int m<int> ( int t@1 ) ; "
7394+ " int x@2 ; "
7395+ " int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7396+ " } ; "
7397+ " } "
7398+ " int N :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }" ;
7399+ ASSERT_EQUALS (exp, tokVarId (code, dinit (CheckOptions, $.templateFullRebuild = true )));
7400+ ASSERT (tokVarId (code).find (" int N :: C :: m<int> ( int t@6 ) { return t@6 + x@2 ; }" ) != std::string::npos);
7401+ }
7402+ { // three levels of nesting
7403+ const char code[] = " namespace A { namespace B {\n "
7404+ " struct C {\n "
7405+ " template<class T> T m(T t);\n "
7406+ " int x;\n "
7407+ " int use(int i) { return m(i); }\n "
7408+ " };\n "
7409+ " } }\n "
7410+ " template<class T> T A::B::C::m(T t) { return t + x; }" ;
7411+ const char exp[] = " namespace A { namespace B { "
7412+ " struct C { "
7413+ " int m<int> ( int t@1 ) ; "
7414+ " int x@2 ; "
7415+ " int use ( int i@3 ) { return m<int> ( i@3 ) ; } "
7416+ " } ; "
7417+ " } } "
7418+ " int A :: B :: C :: m<int> ( int t@4 ) { return t@4 + x@2 ; }" ;
7419+ ASSERT_EQUALS (exp, tokVarId (code, dinit (CheckOptions, $.templateFullRebuild = true )));
7420+ ASSERT (tokVarId (code).find (" int A :: B :: C :: m<int> ( int t@6 ) { return t@6 + x@2 ; }" ) != std::string::npos);
7421+ }
7422+ }
7423+
73277424 void templateTypeDeductionEnum ()
73287425 {
73297426 { // an enum variable has the ValueType of the underlying integer type - the enum
0 commit comments