In C++17, I think GCC is wrong due to the following rules:
temp.type#1
Two template-ids refer to the same class, function, or variable if
1.1 their template-names, operator-function-ids, or literal-operator-ids refer to the same template and
[...]
1.3 their corresponding non-type template arguments of integral or enumeration type have identical values
Formally, a name is used to refer to the entity
basic.concept#5
Every name that denotes an entity is introduced by a declaration.
So, whether Foo<42> or Foo<x>, their template-names all refer to the entity template<decltype(auto) arg> struct Foo {}; we declared. So the bullet 1.1 is first satisfied. Obviously, in this example, the corresponding template-arguments have the identical values, namely 42. At least, according to what the c++17 standard says, they are the equivalence type. Hence GCC is wrong. In addition, GCC 7.5 agrees these types are equivalence.
However, something is changed in the latest draft. It introduces a new wording "template-argument-equivalent".
temp.type#1
Two template-ids are the same if
1.1 their template-names, operator-function-ids, or literal-operator-ids refer to the same template, and
1.2 ...
1.3 their corresponding non-type template-arguments are template-argument-equivalent (see below) after conversion to the type of the template-parameter
And
template-argument-equivalent
Two values are template-argument-equivalent if they are of the same type and
they are of integral type and their values are the same
As said in other answers, the deduced type for Foo<42> is int. Instead, the deduced type for Foo<x> is int const. Although their deduced types are different, However, such a rule should be obeyed:
The top-level cv-qualifiers on the template-parameter are ignored when determining its type.
Hence after conversion to the type of the template-parameter, these two values are of the same type, so they are template-argument-equivalent. So, talking this example under the c++20 standard, GCC is still wrong.
Foo<x>'s template argument is of typeconst int(SoFoo<(const int) 42>), whereasFoo<42>is justintconst int. However, C++ Standard is full of weird cases so I can not be so sure. :)