Bug 109018 - decltype of dependent expressions lookup should be done only when doing template function definition
Summary: decltype of dependent expressions lookup should be done only when doing temp...
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-03-03 22:53 UTC by qingzhe huang
Modified: 2023-03-04 14:06 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description qingzhe huang 2023-03-03 22:53:45 UTC
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()
Comment 1 Andrew Pinski 2023-03-03 23:09:45 UTC
>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.
Comment 2 Andrew Pinski 2023-03-03 23:11:50 UTC
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.
Comment 3 Andrew Pinski 2023-03-03 23:13:26 UTC
(In reply to Andrew Pinski from comment #1)
> template<class T>
> decltype(g(T())) h()
>     return g(T());}
Typo in my example missing {
Comment 4 qingzhe huang 2023-03-04 02:08:50 UTC
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()
Comment 5 Andrew Pinski 2023-03-04 02:14:24 UTC
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.
Comment 6 qingzhe huang 2023-03-04 02:43:14 UTC
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.
Comment 7 qingzhe huang 2023-03-04 12:50:33 UTC
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.
Comment 8 qingzhe huang 2023-03-04 14:06:07 UTC
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)