Issue |
146614
|
Summary |
Regression in Clang 17+ when inheriting operators from recursive base classes
|
Labels |
clang
|
Assignees |
|
Reporter |
eerkela
|
Clang 17.0.1+ segfaults when compiling this simple definition of an overload set:
```
#include <iostream>
#include <utility>
template <typename F, typename... Fs>
struct overloads {
std::decay_t<F> func;
overloads(F&& f) : func(std::move(f)) {}
template <typename... A>
decltype(auto) operator()(A&&... args)
requires (requires{{func(std::forward<A>(args)...)};})
{
return (func(std::forward<A>(args)...));
}
};
template <typename F1, typename F2, typename... Fs>
struct overloads<F1, F2, Fs...> : overloads<F2, Fs...> {
std::decay_t<F1> func;
overloads(F1&& f1, F2&& f2, Fs&&... fs) :
overloads<F2, Fs...>(std::move(f2), std::move(fs)...),
func(std::move(f1))
{}
using overloads<F2, Fs...>::operator(); // <- problem at this line
template <typename... A>
decltype(auto) operator()(A&&... args)
requires (requires{{func(std::forward<A>(args)...)};})
{
return (func(std::forward<A>(args)...));
}
};
template <typename... Fs>
overloads(Fs...) -> overloads<Fs...>;
int main() {
overloads func {
[](int x) { return 0; },
[](const char* y) { return 1; }
};
std::cout << func(1) << "\n";
std::cout << func("abc") << "\n";
}
```
Previous versions of Clang (tested back to 10.0.0 via compiler explorer) and all versions of GCC 10.1+ compile and run just fine, but Clang 17.0.1 and beyond produces the following error:
```
Stack dump:
0. Program arguments: /opt/compiler-explorer/clang-17.0.1/bin/clang-17 -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -mrelax-all -dumpdir /app/output.s- -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name example.cpp -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -fno-verbose-asm -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debug-info-kind=constructor -dwarf-version=4 -debugger-tuning=gdb -fcoverage-compilation-dir=/app -resource-dir /opt/compiler-explorer/clang-17.0.1/lib/clang/17 -internal-isystem /opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0 -internal-isystem /opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/x86_64-linux-gnu -internal-isystem /opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/backward -internal-isystem /opt/compiler-explorer/clang-17.0.1/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=c++20 -fdeprecated-macro -fdebug-compilation-dir=/app -ferror-limit 19 -fgnuc-version=4.2.1 -fno-implicit-modules -fcxx-exceptions -fexceptions -fcolor-diagnostics -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/example-363665.o -x c++ <source>
1. <source>:48:6: current parser token ';'
2. <source>:44:12: parsing function body 'main'
3. <source>:44:12: in compound statement ('{}')
4. <source>:6:8: instantiating class definition 'overloads<(lambda at <source>:46:9), (lambda at <source>:47:9)>'
#0 0x0000000003349f18 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x3349f18)
#1 0x0000000003347adc SignalHandler(int) Signals.cpp:0:0
#2 0x0000767963442520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
#3 0x000000000643acd0 clang::Sema::CheckParameterPacksForExpansion(clang::SourceLocation, clang::SourceRange, llvm::ArrayRef<std::pair<llvm::PointerUnion<clang::TemplateTypeParmType const*, clang::NamedDecl*>, clang::SourceLocation>>, clang::MultiLevelTemplateArgumentList const&, bool&, bool&, std::optional<unsigned int>&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x643acd0)
#4 0x00000000063aaf58 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExprs(clang::Expr* const*, unsigned int, bool, llvm::SmallVectorImpl<clang::Expr*>&, bool*) SemaTemplateInstantiate.cpp:0:0
#5 0x00000000063b2afc clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCallExpr(clang::CallExpr*) SemaTemplateInstantiate.cpp:0:0
#6 0x00000000063a9f86 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr(clang::Expr*) SemaTemplateInstantiate.cpp:0:0
#7 0x00000000063ce908 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformRequiresExpr(clang::RequiresExpr*) SemaTemplateInstantiate.cpp:0:0
#8 0x00000000063a975e clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr(clang::Expr*) SemaTemplateInstantiate.cpp:0:0
#9 0x00000000063b4d65 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformParenExpr(clang::ParenExpr*) SemaTemplateInstantiate.cpp:0:0
#10 0x00000000063a9842 clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr(clang::Expr*) SemaTemplateInstantiate.cpp:0:0
#11 0x00000000063d886f clang::Sema::SubstConstraintExpr(clang::Expr*, clang::MultiLevelTemplateArgumentList const&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x63d886f)
#12 0x0000000005b67257 SubstituteConstraintExpression(clang::Sema&, clang::NamedDecl const*, clang::Expr const*) SemaConcept.cpp:0:0
#13 0x0000000005b69753 clang::Sema::AreConstraintExpressionsEqual(clang::NamedDecl const*, clang::Expr const*, clang::NamedDecl const*, clang::Expr const*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x5b69753)
#14 0x000000000617e373 clang::Sema::IsOverload(clang::FunctionDecl*, clang::FunctionDecl*, bool, bool, bool) (.part.0) SemaOverload.cpp:0:0
#15 0x000000000617ea82 clang::Sema::CheckOverload(clang::Scope*, clang::FunctionDecl*, clang::LookupResult const&, clang::NamedDecl*&, bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x617ea82)
#16 0x0000000005c34848 clang::Sema::CheckFunctionDeclaration(clang::Scope*, clang::FunctionDecl*, clang::LookupResult&, bool, bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x5c34848)
#17 0x000000000643134d clang::TemplateDeclInstantiator::VisitCXXMethodDecl(clang::CXXMethodDecl*, clang::TemplateParameterList*, std::optional<clang::ASTTemplateArgumentListInfo const*>, clang::TemplateDeclInstantiator::RewriteKind) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x643134d)
#18 0x000000000643660d clang::TemplateDeclInstantiator::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x643660d)
#19 0x00000000063c3405 clang::Sema::InstantiateClass(clang::SourceLocation, clang::CXXRecordDecl*, clang::CXXRecordDecl*, clang::MultiLevelTemplateArgumentList const&, clang::TemplateSpecializationKind, bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x63c3405)
#20 0x00000000063df2e5 clang::Sema::InstantiateClassTemplateSpecialization(clang::SourceLocation, clang::ClassTemplateSpecializationDecl*, clang::TemplateSpecializationKind, bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x63df2e5)
#21 0x000000000645ad1f void llvm::function_ref<void ()>::callback_fn<clang::Sema::RequireCompleteTypeImpl(clang::SourceLocation, clang::QualType, clang::Sema::CompleteTypeKind, clang::Sema::TypeDiagnoser*)::'lambda'()>(long) SemaType.cpp:0:0
#22 0x0000000005a14cc1 clang::Sema::runWithSufficientStackSpace(clang::SourceLocation, llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x5a14cc1)
#23 0x000000000645fa3d clang::Sema::RequireCompleteTypeImpl(clang::SourceLocation, clang::QualType, clang::Sema::CompleteTypeKind, clang::Sema::TypeDiagnoser*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x645fa3d)
#24 0x000000000645fac5 clang::Sema::RequireCompleteType(clang::SourceLocation, clang::QualType, clang::Sema::CompleteTypeKind, clang::Sema::TypeDiagnoser&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x645fac5)
#25 0x000000000645fb90 clang::Sema::RequireCompleteType(clang::SourceLocation, clang::QualType, clang::Sema::CompleteTypeKind, unsigned int) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x645fb90)
#26 0x0000000005c284ff clang::Sema::AddInitializerToDecl(clang::Decl*, clang::Expr*, bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x5c284ff)
#27 0x00000000058e8ee1 clang::Parser::ParseDeclarationAfterDeclaratorAndAttributes(clang::Declarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::ForRangeInit*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58e8ee1)
#28 0x00000000058fca30 clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::SourceLocation*, clang::Parser::ForRangeInit*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58fca30)
#29 0x00000000058fddfd clang::Parser::ParseSimpleDeclaration(clang::DeclaratorContext, clang::SourceLocation&, clang::ParsedAttributes&, clang::ParsedAttributes&, bool, clang::Parser::ForRangeInit*, clang::SourceLocation*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58fddfd)
#30 0x00000000058fe2df clang::Parser::ParseDeclaration(clang::DeclaratorContext, clang::SourceLocation&, clang::ParsedAttributes&, clang::ParsedAttributes&, clang::SourceLocation*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58fe2df)
#31 0x00000000059a9a32 clang::Parser::ParseStatementOrDeclarationAfterAttributes(llvm::SmallVector<clang::Stmt*, 32u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*, clang::ParsedAttributes&, clang::ParsedAttributes&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x59a9a32)
#32 0x00000000059aa342 clang::Parser::ParseStatementOrDeclaration(llvm::SmallVector<clang::Stmt*, 32u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x59aa342)
#33 0x00000000059ab149 clang::Parser::ParseCompoundStatementBody(bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x59ab149)
#34 0x00000000059ac40a clang::Parser::ParseFunctionStatementBody(clang::Decl*, clang::Parser::ParseScope&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x59ac40a)
#35 0x00000000058cabc8 clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58cabc8)
#36 0x00000000058fcb79 clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::SourceLocation*, clang::Parser::ForRangeInit*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58fcb79)
#37 0x00000000058c5212 clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58c5212)
#38 0x00000000058c632f clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) (.part.0) Parser.cpp:0:0
#39 0x00000000058cda8f clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58cda8f)
#40 0x00000000058cef0a clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58cef0a)
#41 0x00000000058be95a clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x58be95a)
#42 0x00000000044f8b05 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x44f8b05)
#43 0x0000000003dd5f21 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x3dd5f21)
#44 0x0000000003d5b9fb clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x3d5b9fb)
#45 0x0000000003eb5c23 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0x3eb5c23)
#46 0x0000000000bd50c5 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0xbd50c5)
#47 0x0000000000bce42d ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#48 0x0000000000bd0f24 clang_main(int, char**, llvm::ToolContext const&) (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0xbd0f24)
#49 0x0000000000acf511 main (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0xacf511)
#50 0x0000767963429d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#51 0x0000767963429e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#52 0x0000000000bcdc2e _start (/opt/compiler-explorer/clang-17.0.1/bin/clang-17+0xbcdc2e)
clang++: error: unable to execute command: Segmentation fault (core dumped)
clang++: error: clang frontend command failed due to signal (use -v to see invocation)
```
The only compile option used was `-std=c++20` to access the `requires` clause for the call operators. Interestingly, if the `requires` clause is removed from the base class, then the problem resolves itself for the problematic releases:
```
#include <iostream>
#include <utility>
template <typename F, typename... Fs>
struct overloads {
std::decay_t<F> func;
overloads(F&& f) : func(std::move(f)) {}
template <typename... A>
decltype(auto) operator()(A&&... args)
// requires (requires{{func(std::forward<A>(args)...)};}) // commenting this out fixes the problem
{
return (func(std::forward<A>(args)...));
}
};
template <typename F1, typename F2, typename... Fs>
struct overloads<F1, F2, Fs...> : overloads<F2, Fs...> {
std::decay_t<F1> func;
overloads(F1&& f1, F2&& f2, Fs&&... fs) :
overloads<F2, Fs...>(std::move(f2), std::move(fs)...),
func(std::move(f1))
{}
using overloads<F2, Fs...>::operator(); // <- problem at this line
template <typename... A>
decltype(auto) operator()(A&&... args)
requires (requires{{func(std::forward<A>(args)...)};})
{
return (func(std::forward<A>(args)...));
}
};
template <typename... Fs>
overloads(Fs...) -> overloads<Fs...>;
int main() {
overloads func {
[](int x) { return 0; },
[](const char* y) { return 1; }
};
std::cout << func(1) << "\n";
std::cout << func("abc") << "\n";
}
```
Which suggests the regression has something to do with applying template constraints for recursively inherited methods with a `using` statement.
I encountered this problem while trying to build a simple overload set that retains lvalue references for the component functions, which currently seems to be impossible in clang due to this bug. A simpler fold _expression_ such as this:
```
template <typename... Fs>
struct overloads : public Fs... { using Fs::operator()...; };
```
... Would work, but the inheritance forces all `Fs...` to be non-reference types, therefore forcing an extra copy when the overload set is constructed, which I'd like to avoid as long as lifetimes would not be an issue. Is there any workaround while this bug is being resolved, or hope of a backport to the problematic releases once it's fixed?
Godbolt:
https://godbolt.org/z/W5hrb4559
Thank you for all your work!
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs