llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.51k stars 11.79k forks source link

Windows Clang finds ambiguity after function template call site #91300

Open joshuamaiche opened 5 months ago

joshuamaiche commented 5 months ago

The repro is more straightforward than the tile.

main.cpp

int GetVal1() { return 0; }

template <typename T> int GetVal2() { return GetVal1(); }

int result1 = GetVal2<int>(); // Does not compile
int result2 = GetVal1(); // Compiles

int GetVal1(int value = 0) { return 0; }

int main() {}

Console

> clang main.cpp
main.cpp:3:46: error: call to 'GetVal1' is ambiguous
template <typename T> int GetVal2() { return GetVal1(); }
                                             ^~~~~~~
main.cpp:1:5: note: candidate function
int GetVal1() { return 0; }
    ^
main.cpp:8:5: note: candidate function
int GetVal1(int value = 0) { return 0; }
    ^
1 error generated.

GetVal1() is defined, then overloaded in an ambiguous manner. However, before this ambiguity is introduced, the function template GetVal2(), which uses GetVal1(), is defined and called. This still results in the compiler complaining that the call to GetVal1() is ambiguous.

I'm not certain if this is allowed by the standard, but it's at least inconsistent between different flavors of Clang. The same major version of Linux Clang compiles the code without any errors.

This retroactive ambiguity means that files included later can interfere with files included earlier in code.

Windows console

> clang --version
clang version 16.0.5
Target: i686-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\bin

Linux console

$ clang-16 --version
Ubuntu clang version 16.0.6 (++20231112100510+7cbf1a259152-1~exp1~20231112100554.106)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
llvmbot commented 5 months ago

@llvm/issue-subscribers-c-1

Author: None (joshuamaiche)

The repro is more straightforward than the tile. **main.cpp** ``` int GetVal1() { return 0; } template <typename T> int GetVal2() { return GetVal1(); } int result1 = GetVal2<int>(); // Does not compile int result2 = GetVal1(); // Compiles int GetVal1(int value = 0) { return 0; } int main() {} ``` **Console** ``` >clang main.cpp main.cpp:3:46: error: call to 'GetVal1' is ambiguous template <typename T> int GetVal2() { return GetVal1(); } ^~~~~~~ main.cpp:1:5: note: candidate function int GetVal1() { return 0; } ^ main.cpp:8:5: note: candidate function int GetVal1(int value = 0) { return 0; } ^ 1 error generated. ``` `GetVal1()` is defined, then overloaded in an ambiguous manner. However, before this ambiguity is introduced, the function template `GetVal2()`, which uses `GetVal1()`, is defined and called. This still results in the compiler complaining that the call to `GetVal1()` is ambiguous. I'm not certain if this is allowed by the standard, but it's at least inconsistent between different flavors of Clang. The same major version of Linux Clang compiles the code without any errors. This retroactive ambiguity means that files included later can interfere with files included earlier in code. **Windows console** ``` >clang --version clang version 16.0.5 Target: i686-pc-windows-msvc Thread model: posix InstalledDir: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\bin ``` **Linux console** ``` $ clang-16 --version Ubuntu clang version 16.0.6 (++20231112100510+7cbf1a259152-1~exp1~20231112100554.106) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin ```
asl commented 5 months ago

Likely due to delayed template parsing that is enabled for MSVC compatibility (see https://clang.llvm.org/docs/MSVCCompatibility.html)

joshuamaiche commented 5 months ago

Likely due to delayed template parsing that is enabled for MSVC compatibility

You're totally right! Compiling with -fno-delayed-template-parsing allows the code to compile. This also tracks with the repro also failing on MSVC, but compiling on GCC.

However, it sounds like delayed template parsing might not be behaving the way it should. The page you linked says:

MSVC appears to defer all parsing an analysis of inline method bodies in class templates until instantiation time.

Since the template is instantiated before the ambiguity is introduced, I would expect it to still compile. On the other hand, maybe it's best to stay with MSVC's behavior?

template <typename T> int GetVal2() { return GetVal1(); }
int result1 = GetVal2<int>();
int GetVal1(int value = 0) { return 0; }
int main() {}

This case compiles on MSVC, so it seems like Clang's -fdelayed-template-parsing might need to keep its current behavior where it parses the template even after instantiation?

EugeneZelenko commented 5 months ago

Could you please try 18 or main branch? https://godbolt.org should be helpful.

llvmbot commented 5 months ago

@llvm/issue-subscribers-clang-frontend

Author: None (joshuamaiche)

The repro is more straightforward than the tile. **main.cpp** ```cpp int GetVal1() { return 0; } template <typename T> int GetVal2() { return GetVal1(); } int result1 = GetVal2<int>(); // Does not compile int result2 = GetVal1(); // Compiles int GetVal1(int value = 0) { return 0; } int main() {} ``` **Console** ```console > clang main.cpp main.cpp:3:46: error: call to 'GetVal1' is ambiguous template <typename T> int GetVal2() { return GetVal1(); } ^~~~~~~ main.cpp:1:5: note: candidate function int GetVal1() { return 0; } ^ main.cpp:8:5: note: candidate function int GetVal1(int value = 0) { return 0; } ^ 1 error generated. ``` `GetVal1()` is defined, then overloaded in an ambiguous manner. However, before this ambiguity is introduced, the function template `GetVal2()`, which uses `GetVal1()`, is defined and called. This still results in the compiler complaining that the call to `GetVal1()` is ambiguous. I'm not certain if this is allowed by the standard, but it's at least inconsistent between different flavors of Clang. The same major version of Linux Clang compiles the code without any errors. This retroactive ambiguity means that files included later can interfere with files included earlier in code. **Windows console** ```console > clang --version clang version 16.0.5 Target: i686-pc-windows-msvc Thread model: posix InstalledDir: C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\bin ``` **Linux console** ```console $ clang-16 --version Ubuntu clang version 16.0.6 (++20231112100510+7cbf1a259152-1~exp1~20231112100554.106) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin ```
joshuamaiche commented 5 months ago

Could you please try 18 or main branch?

Confirmed it also occurs on 18 when using -fdelayed-template-parsing.

Since this matches MSVC behavior though, and that's the point of this option, I would suggest the best fix would be to maintain the current functionality but update https://clang.llvm.org/docs/MSVCCompatibility.html and any other documentation to clarify that -fdelayed-template-parsing parses the template even later than at template instantiation.