Bug 87709 - c++17 class template argument deduction not working in a very specific case
Summary: c++17 class template argument deduction not working in a very specific case
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 8.2.0
: P3 normal
Target Milestone: 12.0
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
: 87712 88020 88762 89220 96878 (view as bug list)
Depends on:
Blocks:
 
Reported: 2018-10-23 14:47 UTC by Baruch Burstein
Modified: 2021-08-17 05:07 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-11-14 00:00:00


Attachments
test case (252 bytes, text/plain)
2018-10-23 14:47 UTC, Baruch Burstein
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Baruch Burstein 2018-10-23 14:47:30 UTC
Created attachment 44883 [details]
test case

The test case is attached. It has no includes and so I am attaching the *.cpp file as per https://gcc.gnu.org/bugs/

I am compiling with -std=c++17

Output of g++ -v:

# g++ -v
Using built-in specs.
COLLECT_GCC=C:\MyProgs\msys64\mingw64\bin\g++.exe
COLLECT_LTO_WRAPPER=C:/MyProgs/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/8.2.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../gcc-8.2.0/configure --prefix=/mingw64 --with-local-prefix=/mingw64/local --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --with-native-system-header-dir=/mingw64/x86_64-w64-mingw32/include --libexecdir=/mingw64/lib --enable-bootstrap --with-arch=x86-64 --with-tune=generic --enable-languages=ada,c,lto,c++,objc,obj-c++,fortran --enable-shared --enable-static --enable-libatomic --enable-threads=posix --enable-graphite --enable-fully-dynamic-string --enable-libstdcxx-filesystem-ts=yes --enable-libstdcxx-time=yes --disable-libstdcxx-pch --disable-libstdcxx-debug --disable-isl-version-check --enable-lto --enable-libgomp --disable-multilib --enable-checking=release --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-libiconv --with-system-zlib --with-gmp=/mingw64 --with-mpfr=/mingw64 --with-mpc=/mingw64 --with-isl=/mingw64 --with-pkgversion='Rev3, Built by MSYS2 project' --with-bugurl=https://sourceforge.net/projects/msys2 --with-gnu-as --with-gnu-ld
Thread model: posix
gcc version 8.2.0 (Rev3, Built by MSYS2 project)


I tried with another build and got the same results. This one gave:

# g++ -v
Using built-in specs.
COLLECT_GCC=/opt/wandbox/gcc-8.2.0/bin/g++
COLLECT_LTO_WRAPPER=/opt/wandbox/gcc-8.2.0/libexec/gcc/x86_64-pc-linux-gnu/8.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../gcc-8.2.0/configure --prefix=/opt/wandbox/gcc-8.2.0 --enable-languages=c,c++ --disable-multilib --without-ppl --without-cloog-ppl --enable-checking=release --disable-nls LDFLAGS=-Wl,-rpath,/opt/wandbox/gcc-8.2.0/lib,-rpath,/opt/wandbox/gcc-8.2.0/lib64,-rpath,/opt/wandbox/gcc-8.2.0/lib32 --enable-lto
Thread model: posix
gcc version 8.2.0 (GCC)
Comment 1 Jonathan Wakely 2018-10-23 15:41:30 UTC
Confirmed, reduced:

template <class T>
struct lit {
  lit(T) { }
};

template <class T>
int operator+(lit<T>, lit<T>) {
  return 0;
}

auto r2 = (lit(0)) + lit(0);



11 | auto r2 = (lit(0)) + lit(0);
   |            ^~~
ctad.cc:2:8: note: 'template<class T> struct lit' declared here
2 | struct lit {
  |        ^~~
Comment 2 Jonathan Wakely 2018-10-23 15:53:52 UTC
Oops, I missed the first line of the diagnostic. The error from trunk is:


ctad.cc:11:12: error: missing template arguments after 'lit'
11 | auto r2 = (lit(0)) + lit(0);
   |            ^~~
ctad.cc:2:8: note: 'template<class T> struct lit' declared here
2 | struct lit {
  |        ^~~
Comment 3 Jonathan Wakely 2018-10-23 17:33:15 UTC
*** Bug 87712 has been marked as a duplicate of this bug. ***
Comment 4 Baruch Burstein 2018-10-25 22:33:46 UTC
There is a pretty good (speculative) analysis of this issue here: https://stackoverflow.com/a/52986284/331785
I am copying it to here for completeness, but credit for this goes to the author of that post:

There are two kinds of expressions that look similar but have vastly different meaning:

(type) + expr
(expr) + expr

The first is a C-style cast expression, that converts the unary expression + expr to type, the second is a binary expression that performs addition.

To disambiguate an expression of form (something) + expr, GCC first assumes that something is a type and does a tentative parse. If that succeeds, then the whole expression is treated as a cast expression, otherwise, something is reparsed as an expression.

Now here's where I think the bug resides: during the tentative parse, GCC wrongly believes that class template argument deduction (CTAD) cannot appear, so it issues an error when it appears. But in fact, even though the tentative parse will definitely fail in this case, something may still be a valid function-style cast expression, and thus the reparse might be successful.

For cat((lit('b')), lit('d')), lit('b') + lit('d'), and (lit('b')), GCC is clever enough to see that they can't be C-style cast expression, so it does not do the tentative parse. For (lit<char>('b')) + lit('d'), there's no CTAD in lit<char>('b'), so it is fine as well.

Prove of the above analysis:

If + is changed to / (or most operators other than -, * or &), no error occurs, because (something) / expr can't be a valid cast expression.

Similar ambiguity exists in sizeof(something) (could be sizeof(type) or sizeof(expr)), and as expected, sizeof(lit(0)) triggers a similar error.
Comment 5 Jonathan Wakely 2018-11-14 14:00:58 UTC
*** Bug 88020 has been marked as a duplicate of this bug. ***
Comment 6 Jonathan Wakely 2018-11-14 14:01:43 UTC
From Bug 88020:

template <class T>
struct S{
    S(T){}
};
static_assert(sizeof(S{0}));



<source>:5:22: error: missing template arguments after 'S'
 static_assert(sizeof(S{0}));
                      ^
<source>:2:8: note: 'template<class T> struct S' declared here
 struct S{
        ^
Comment 7 Jonathan Wakely 2019-02-06 10:07:23 UTC
*** Bug 89220 has been marked as a duplicate of this bug. ***
Comment 8 Marek Polacek 2021-04-22 16:10:05 UTC
This was ASSIGNED but no actual assignee.
Comment 9 Patrick Palka 2021-04-22 16:22:57 UTC
Whoops, sorry about that, I meant to assign myself.  Trying again..
Comment 10 GCC Commits 2021-04-24 04:14:57 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:5f1a2cb9c2dc09eed53da5d5787d14bec700b10b

commit r12-99-g5f1a2cb9c2dc09eed53da5d5787d14bec700b10b
Author: Patrick Palka <ppalka@redhat.com>
Date:   Sat Apr 24 00:01:42 2021 -0400

    c++: Hard error with tentative parse and CTAD [PR87709]
    
    When parsing e.g. the operand of sizeof, where both types and
    expressions are accepted, if during the tentative type parse we
    encounter an unexpected template placeholder, we must simulate
    an error rather than issue a real error because the expression
    parse can still succeed.
    
    gcc/cp/ChangeLog:
    
            PR c++/87709
            * parser.c (cp_parser_type_id_1): If we see a template
            placeholder, first try simulating an error before issuing
            a real error.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/87709
            * g++.dg/cpp1z/class-deduction86.C: New test.
Comment 11 Andrew Pinski 2021-08-04 17:59:00 UTC
Fixed.
Comment 12 Andrew Pinski 2021-08-17 05:06:42 UTC
*** Bug 96878 has been marked as a duplicate of this bug. ***
Comment 13 Andrew Pinski 2021-08-17 05:07:47 UTC
*** Bug 88762 has been marked as a duplicate of this bug. ***