Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4890,10 +4890,13 @@ // Introduce a new scope where local variable instantiations will be // recorded, unless we're actually a member function within a local // class, in which case we need to merge our results with the parent - // scope (of the enclosing function). + // scope (of the enclosing function). The exception is instantiating + // a function template specialization, since the template to be + // instantiated already has references to locals properly substituted. bool MergeWithParentScope = false; if (CXXRecordDecl *Rec = dyn_cast(Function->getDeclContext())) - MergeWithParentScope = Rec->isLocalClass(); + MergeWithParentScope = + Rec->isLocalClass() && !Function->isFunctionTemplateSpecialization(); LocalInstantiationScope Scope(*this, MergeWithParentScope); Index: clang/test/SemaCXX/recursive-lambda.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/recursive-lambda.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s + +// expected-no-diagnostics + +// Check recursive instantiation of lambda does not cause assertion. +// lambda function `f` in `fun1` is instantiated twice: first +// as f(f, Number<1>), then as f(f, Number<0>). The +// LocalInstantiationScopes of these two instantiations both contain +// `f` and `i`. However, since they are not merged, clang should not +// assert for that. + +template +struct Number +{ + static constexpr unsigned value = v; +}; + +template +constexpr auto fun1(Number = Number<0>{}, Number = Number<1>{}) +{ + constexpr unsigned a = 0; + auto f = [&](auto fs, auto i) { + if constexpr(i.value > 0) + { + (void)a; + return fs(fs, Number{}); + } + (void)a; + }; + + return f(f, Number{}); +} + + +void fun2() { + fun1(); +}