In cppreference.com(https://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading) gives this example about dependent expression lookup issue. However, it turns out only GCC has this parsing issue. Both clang and MSVC can solve this with no issue.(https://www.godbolt.org/z/9anx871rr) template<class T> decltype(g(T())) h(); // decltype(g(T())) is a dependent type int g(int); template<class T> decltype(g(T())) h() { // redeclaration of h() uses earlier lookup return g(T()); // although the lookup here does find g(int) } int i = h<int>(); // template argument substitution fails; g(int) // was not in scope at the first declaration of h()
>gives this example about dependent expression lookup issue. Yes and it explictly it is about an invalid code example which GCC gets correct. The comment here: // template argument substitution fails; g(int) // was not in scope at the first declaration of h() Explictly saying this code is invalid. If you used a non-base type. ADL will find the g correctly in the substitution of T. E.g. this is valid: ``` struct A{}; template<class T> decltype(g(T())) h(); int g(A); template<class T> decltype(g(T())) h() return g(T());} int i = h<A>(); ``` >Both clang and MSVC can solve this with no issue. Yes and clang and MSVC has a bug then.
Also from that page: > If multiple declarations of the same template differ in the result of name lookup, the first such declaration is used That is GCC gets that part correct even while clang and MSVC does not.
(In reply to Andrew Pinski from comment #1) > template<class T> > decltype(g(T())) h() > return g(T());} Typo in my example missing {
Thank you for your detailed explanation and really appreciate it. My only pleading argument is that the root cause of error is from GCC cannot find correct prototype in "template declaration" which is __NOT__ mandatory for template definition. For example, if I commented out the invalid template function declaration, the parser works without issue. (https://www.godbolt.org/z/1qnTnrE1Y). So, my argument is that GCC report error due to something which is not always necessary. Why cannot parser postpone reporting when actually template definition starts? Even there is not declaration, definition has all information to work. This will pass compilation if template declaration is commented: // template<class T> // decltype(g(T())) h(); // decltype(g(T())) is a dependent type int g(int); template<class T> decltype(g(T())) h() { // redeclaration of h() uses earlier lookup return g(T()); // although the lookup here does find g(int) } int i = h<int>(); // template argument substitution fails; g(int) // was not in scope at the first declaration of h()
Note g can be still found after the declaration via argument dependent lookup. Just int is a base type so it does not have a namespace associated with it.
I agree "Note g can be still found after the declaration via argument dependent lookup." Can we view this issue from a different angle? The real work of parsing should be started at "template function definition". So, if the "template function declaration" is invalid because of "scope" of "g", then the "valid" definition DOES NOT belong to the "invalid" declaration. i.e. There are tons of invalid declaration are discarded as they are not required for template function instantiation.
I find another argument from decltype in cppreference (https://en.cppreference.com/w/cpp/language/decltype#Explanation) quote: Because no temporary object is created, the type need not be complete or have an available destructor, and can be abstract. This rule doesn't apply to sub-expressions: in decltype(f(g())), g() must have a complete type, but f() need not. Does this mean that here "f" needs not be available when "g" is int which is always complete? So, in our example the function "g" (aka "f" here) needs not be available.
I gdb a little bit and I feel this issue is fixable. See the comparison of "unq" and "function" below is not compatible because "function" is "IDENTIFIER_NODE" and "unq" is "VIEW_CONVERT_EXPR". If we check and convert "unq", we will find they are matching. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000d0bafc in tsubst_copy_and_build(tree_node*, tree_node*, int, tree_node*, bool, bool) at /home/nick/Downloads/gcc-dev/gcc/gcc/cp/pt.cc:20608 breakpoint already hit 1 time (gdb) l 20608 20603 (function, args, complain, in_decl, true, 20604 integral_constant_expression_p)); 20605 if (unq == error_mark_node) 20606 RETURN (error_mark_node); 20607 20608 if (unq != function) 20609 { 20610 char const *const msg 20611 = G_("%qD was not declared in this scope, " 20612 "and no declarations were found by " (gdb) p IDENTIFIER_POINTER(function) $16 = 0x7ffff70c42c0 "g" (gdb) p IDENTIFIER_POINTER(DECL_NAME(TREE_OPERAND(unq,0))) $17 = 0x7ffff70c42c0 "g" (gdb) p TREE_CODE(function) $18 = IDENTIFIER_NODE (gdb) p TREE_CODE(unq) $19 = VIEW_CONVERT_EXPR (gdb)