tahonermann created this revision. tahonermann added reviewers: erichkeane, aaron.ballman, shafik. Herald added a project: All. tahonermann requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Previously, a lambda expression in a dependent context with a default argument containing an immediately invoked lambda expression would produce a closure class object that, if invoked such that the default argument was used, resulted in a compiler crash or one of the following assertion failures during code generation. The failures occurred regardless of whether the lambda expressions were dependent. clang/lib/CodeGen/CGCall.cpp: Assertion `(isGenericMethod || Ty->isVariablyModifiedType() || Ty.getNonReferenceType()->isObjCRetainableType() || getContext() .getCanonicalType(Ty.getNonReferenceType()) .getTypePtr() == getContext().getCanonicalType((*Arg)->getType()).getTypePtr()) && "type mismatch in call argument!"' failed. clang/lib/AST/Decl.cpp: Assertion `!Init->isValueDependent()' failed. Default arguments in declarations in local context are instantiated along with their enclosing function or variable template (since such declarations can't be explicitly specialized). Previously, such instantiations were performed at the same time that their associated parameters were instantiated. However, that approach fails in cases like the following in which the context for the inner lambda is the outer lambda, but construction of the outer lambda is dependent on the parameters of the inner lambda. This change resolves this dependency by delaying instantiation of default arguments in local contexts until after construction of the enclosing context. template <typename T> auto f() { return [](T = []{ return T{}; }()) { return 0; }; } Refactoring included with this change results in the same code now being used to instantiate default arguments that appear in local context and those that are only instantiated when used at a call site; previously, such code was duplicated and out of sync. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D133500 Files: clang/include/clang/Sema/Sema.h clang/lib/AST/DeclBase.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp clang/test/CodeGenCXX/mangle-lambdas-cxx14.cpp clang/test/CodeGenCXX/mangle-lambdas-cxx20.cpp clang/test/CodeGenCXX/mangle-lambdas.cpp clang/test/SemaCXX/vartemplate-lambda.cpp clang/test/SemaTemplate/default-arguments.cpp clang/test/SemaTemplate/instantiate-local-class.cpp
Index: clang/test/SemaTemplate/instantiate-local-class.cpp =================================================================== --- clang/test/SemaTemplate/instantiate-local-class.cpp +++ clang/test/SemaTemplate/instantiate-local-class.cpp @@ -401,7 +401,8 @@ namespace PR21332 { template<typename T> void f1() { struct S { // expected-note{{in instantiation of member class 'S' requested here}} - void g1(int n = T::error); // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} + void g1(int n = T::error); // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} \ + // expected-note {{in instantiation of default function argument expression for 'g1<int>' required here}} }; } template void f1<int>(); // expected-note{{in instantiation of function template specialization 'PR21332::f1<int>' requested here}} @@ -438,7 +439,8 @@ class S { // expected-note {{in instantiation of member function 'PR21332::f6()::S::get' requested here}} void get() { class S2 { // expected-note {{in instantiation of member class 'S2' requested here}} - void g1(int n = T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} + void g1(int n = T::error); // expected-error {{type 'int' cannot be used prior to '::' because it has no members}} \ + // expected-note {{in instantiation of default function argument expression for 'g1<int>' required here}} }; } }; @@ -460,16 +462,18 @@ template <typename T> void foo() { struct Inner { // expected-note {{in instantiation}} - void operator()(T a = "") {} // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} - // expected-note@-1 {{passing argument to parameter 'a' here}} + void operator()(T a = "") {} // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} \ + // expected-note {{in instantiation of default function argument expression for 'operator()<rdar23721638::A>' required here}} \ + // expected-note {{passing argument to parameter 'a' here}} }; - Inner()(); // expected-error {{type 'Inner' does not provide a call operator}} + Inner()(); } - template void foo<A>(); // expected-note 2 {{in instantiation}} + template void foo<A>(); // expected-note {{in instantiation}} template <typename T> void bar() { - auto lambda = [](T a = "") {}; // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} - // expected-note@-1 {{passing argument to parameter 'a' here}} + auto lambda = [](T a = "") {}; // expected-error {{conversion function from 'const char[1]' to 'rdar23721638::A' invokes a deleted function}} \ + // expected-note {{in instantiation of default function argument expression for 'operator()<rdar23721638::A>' required here}} \ + // expected-note {{passing argument to parameter 'a' here}} lambda(); } template void bar<A>(); // expected-note {{in instantiation}} @@ -490,7 +494,8 @@ template <typename T> void f(int x = [](T x = nullptr) -> int { return x; }()); // expected-error@-1 {{cannot initialize a parameter of type 'int' with an rvalue of type 'std::nullptr_t'}} - // expected-note@-2 {{passing argument to parameter 'x' here}} + // expected-note@-2 {{in instantiation of default function argument expression for 'operator()<int>' required here}} + // expected-note@-3 {{passing argument to parameter 'x' here}} void g() { f<int>(); } // expected-note@-1 {{in instantiation of default function argument expression for 'f<int>' required here}} Index: clang/test/SemaTemplate/default-arguments.cpp =================================================================== --- clang/test/SemaTemplate/default-arguments.cpp +++ clang/test/SemaTemplate/default-arguments.cpp @@ -169,7 +169,8 @@ namespace NondefDecls { template<typename T> void f1() { - int g1(int defarg = T::error); // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} + int g1(int defarg = T::error); // expected-error{{type 'int' cannot be used prior to '::' because it has no members}} \ + // expected-note {{in instantiation of default function argument expression for 'g1<int>' required here}} } template void f1<int>(); // expected-note{{in instantiation of function template specialization 'NondefDecls::f1<int>' requested here}} } Index: clang/test/SemaCXX/vartemplate-lambda.cpp =================================================================== --- clang/test/SemaCXX/vartemplate-lambda.cpp +++ clang/test/SemaCXX/vartemplate-lambda.cpp @@ -6,7 +6,8 @@ template<typename T> auto fn1 = [](auto a) { return a + T(1); }; template<typename T> auto v1 = [](int a = T()) { return a; }(); // expected-error@-1{{cannot initialize a parameter of type 'int' with an rvalue of type 'int *'}} -// expected-note@-2{{passing argument to parameter 'a' here}} +// expected-note@-2{{in instantiation of default function argument expression for 'operator()<int *>' required here}} +// expected-note@-3{{passing argument to parameter 'a' here}} struct S { template<class T> Index: clang/test/CodeGenCXX/mangle-lambdas.cpp =================================================================== --- clang/test/CodeGenCXX/mangle-lambdas.cpp +++ clang/test/CodeGenCXX/mangle-lambdas.cpp @@ -210,6 +210,84 @@ } int k = testVarargsLambdaNumbering(); + +template<typename = int> +void ft1(int = [](int p = [] { return 42; } ()) { + return p; + } ()); +void test_ft1() { + // CHECK: call noundef i32 @"_ZZZ3ft1IiEviENK3$_4clEiEd_NKUlvE_clEv" + // CHECK: call noundef i32 @"_ZZ3ft1IiEviENK3$_4clEi" + ft1(); +} +// CHECK-LABEL: define internal noundef i32 @"_ZZ3ft1IiEviENK3$_4clEi" +// CHECK-LABEL: define internal noundef i32 @"_ZZZ3ft1IiEviENK3$_4clEiEd_NKUlvE_clEv" + +struct c1 { + template<typename = int> + void mft1(int = [](int p = [] { return 42; } ()) { + return p; + } ()); +}; +void test_c1_mft1() { + // CHECK: call noundef i32 @_ZZZN2c14mft1IiEEviEd_NKUliE_clEiEd_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZN2c14mft1IiEEviEd_NKUliE_clEi + c1{}.mft1(); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN2c14mft1IiEEviEd_NKUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZN2c14mft1IiEEviEd_NKUliE_clEiEd_NKUlvE_clEv + +template<typename = int> +struct ct1 { + void mf1(int = [](int p = [] { return 42; } ()) { + return p; + } ()); + friend void ff(ct1, int = [](int p = [] { return 0; }()) { return p; }()) {} +}; +void test_ct1_mft1() { + // CHECK: call noundef i32 @_ZZZN3ct1IiE3mf1EiEd_NKUliE_clEiEd_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZN3ct1IiE3mf1EiEd_NKUliE_clEi + ct1<>{}.mf1(); + // CHECK: call noundef i32 @_ZZZ2ff3ct1IiEiEd_NKUliE_clEiEd_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZ2ff3ct1IiEiEd_NKUliE_clEi + ff(ct1<>{}); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN3ct1IiE3mf1EiEd_NKUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZN3ct1IiE3mf1EiEd_NKUliE_clEiEd_NKUlvE_clEv +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ2ff3ct1IiEiEd_NKUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ2ff3ct1IiEiEd_NKUliE_clEiEd_NKUlvE_clEv + +template<typename = int> +void ft2() { + [](int p = [] { return 42; } ()) { return p; } (); +} +template void ft2<>(); +// CHECK: call noundef i32 @_ZZZ3ft2IiEvvENKUliE_clEiEd_NKUlvE_clEv +// CHECK: call noundef i32 @_ZZ3ft2IiEvvENKUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ3ft2IiEvvENKUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ3ft2IiEvvENKUliE_clEiEd_NKUlvE_clEv + +template<typename> +void ft3() { + void f(int = []{ return 0; }()); + f(); +} +template void ft3<int>(); +// CHECK: call noundef i32 @"_ZZ1fiENK3$_5clEv" +// CHECK-LABEL: define internal noundef i32 @"_ZZ1fiENK3$_5clEv" + +template<typename> +void ft4() { + struct lc { + void mf(int = []{ return 0; }()) {} + }; + lc().mf(); +} +template void ft4<int>(); +// CHECK: call noundef i32 @_ZZZ3ft4IiEvvEN2lc2mfEiEd_NKUlvE_clEv +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ3ft4IiEvvEN2lc2mfEiEd_NKUlvE_clEv + + // Check linkage of the various lambdas. // CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ11inline_funciENKUlvE_clEv // CHECK: ret i32 1 Index: clang/test/CodeGenCXX/mangle-lambdas-cxx20.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/mangle-lambdas-cxx20.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -no-opaque-pointers -std=c++20 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s -w | FileCheck %s + +template <typename T> +auto ft1() { + return []<typename U = T>(T p1 = [] { return T{}; } (), + U p2 = [] { return U{}; } ()) { return p1+p2; }; +} +void test_ft1() { + // CHECK: call noundef i32 @_ZZZ3ft1IiEDavENKUlTyiT_E_clIiEEDaiS0_Ed0_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZZ3ft1IiEDavENKUlTyiT_E_clIiEEDaiS0_Ed_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZ3ft1IiEDavENKUlTyiT_E_clIiEEDaiS0_ + ft1<int>()(); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ3ft1IiEDavENKUlTyiT_E_clIiEEDaiS0_ +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ3ft1IiEDavENKUlTyiT_E_clIiEEDaiS0_Ed0_NKUlvE_clEv +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ3ft1IiEDavENKUlTyiT_E_clIiEEDaiS0_Ed_NKUlvE_clEv + +template <typename T> +auto vt1 = []<typename U = T>(T p1 = [] { return T{}; } (), + U p2 = [] { return U{}; } ()) { return p1+p2; }; +void test_vt1() { + // CHECK: call noundef i32 @_ZZNK3vt1IiEMUlTyiT_E_clIiEEDaiS1_Ed0_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZNK3vt1IiEMUlTyiT_E_clIiEEDaiS1_Ed_NKUlvE_clEv + // CHECK: call noundef i32 @_ZNK3vt1IiEMUlTyiT_E_clIiEEDaiS1_ + vt1<int>(); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZNK3vt1IiEMUlTyiT_E_clIiEEDaiS1_ +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZNK3vt1IiEMUlTyiT_E_clIiEEDaiS1_Ed0_NKUlvE_clEv +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZNK3vt1IiEMUlTyiT_E_clIiEEDaiS1_Ed_NKUlvE_clEv Index: clang/test/CodeGenCXX/mangle-lambdas-cxx14.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/mangle-lambdas-cxx14.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -no-opaque-pointers -std=c++14 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s -w | FileCheck %s + +template<typename T = int> +auto ft1() { + return [](int p = [] { return 0; } ()) { return p; }; +} +void test_ft1() { + // CHECK: call noundef i32 @_ZZZ3ft1IiEDavENKUliE_clEiEd_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZ3ft1IiEDavENKUliE_clEi + ft1<>()(); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ3ft1IiEDavENKUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ3ft1IiEDavENKUliE_clEiEd_NKUlvE_clEv + +template <typename T> +auto ft2() { + struct S { + T operator()(T p = []{ return 0; }()) const { return p; } + }; + return S{}; +} +void test_ft2() { + // CHECK: call noundef i32 @_ZZZ3ft2IiEDavENK1SclEiEd_NKUlvE_clEv + // CHECK: call noundef i32 @_ZZ3ft2IiEDavENK1SclEi + ft2<int>()(); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ3ft2IiEDavENK1SclEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZZ3ft2IiEDavENK1SclEiEd_NKUlvE_clEv + +template <typename> +auto vt1 = [](int p = [] { return 0; } ()) { return p; }; +void test_vt1() { + // CHECK: call noundef i32 @_ZZNK3vt1IiEMUliE_clEiEd_NKUlvE_clEv + // CHECK: call noundef i32 @_ZNK3vt1IiEMUliE_clEi + vt1<int>(); +} +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZNK3vt1IiEMUliE_clEi +// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZNK3vt1IiEMUliE_clEiEd_NKUlvE_clEv Index: clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp =================================================================== --- clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp +++ clang/test/CXX/expr/expr.prim/expr.prim.lambda/default-arguments.cpp @@ -34,7 +34,8 @@ template<typename T> void defargs_in_template_unused(T t) { - auto l1 = [](const T& value = T()) { }; // expected-error{{no matching constructor for initialization of 'NoDefaultCtor'}} + auto l1 = [](const T& value = T()) { }; // expected-error{{no matching constructor for initialization of 'NoDefaultCtor'}} \ + // expected-note {{in instantiation of default function argument expression for 'operator()<NoDefaultCtor>' required here}} l1(t); } @@ -44,13 +45,8 @@ template<typename T> void defargs_in_template_used() { auto l1 = [](const T& value = T()) { }; // expected-error{{no matching constructor for initialization of 'NoDefaultCtor'}} \ - // expected-note{{candidate function not viable: requires single argument 'value', but no arguments were provided}} -#if defined(_WIN32) && !defined(_WIN64) - // expected-note@46{{conversion candidate of type 'void (*)(const NoDefaultCtor &) __attribute__((thiscall))'}} -#else - // expected-note@46{{conversion candidate of type 'void (*)(const NoDefaultCtor &)'}} -#endif - l1(); // expected-error{{no matching function for call to object of type '(lambda at }} + // expected-note {{in instantiation of default function argument expression for 'operator()<NoDefaultCtor>' required here}} + l1(); } template void defargs_in_template_used<NonPOD>(); Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1994,7 +1994,7 @@ /// Normal class members are of more specific types and therefore /// don't make it here. This function serves three purposes: /// 1) instantiating function templates -/// 2) substituting friend declarations +/// 2) substituting friend and local function declarations /// 3) substituting deduction guide declarations for nested class templates Decl *TemplateDeclInstantiator::VisitFunctionDecl( FunctionDecl *D, TemplateParameterList *TemplateParams, @@ -2134,6 +2134,9 @@ assert(D->getDeclContext()->isFileContext()); LexicalDC = D->getDeclContext(); } + else if (D->isLocalExternDecl()) { + LexicalDC = SemaRef.CurContext; + } Function->setLexicalDeclContext(LexicalDC); @@ -2276,6 +2279,38 @@ QualifierLoc.hasQualifier()); } + // Per [temp.inst], default arguments in function declarations at local scope + // are instantiated along with the enclosing declaration. For example: + // + // template<typename T> + // void ft() { + // void f(int = []{ return T::value; }()); + // } + // template void ft<int>(); // error: type 'int' cannot be used prior + // to '::' because it has no members + // + // The error is issued during instantiation of ft<int>() because substitution + // into the default argument fails; the default argument is instantiated even + // though it is never used. + if (Function->isLocalExternDecl()) { + for (ParmVarDecl *PVD : Function->parameters()) { + if (!PVD->hasDefaultArg()) + continue; + if (SemaRef.SubstDefaultArgument(D->getInnerLocStart(), PVD, TemplateArgs)) { + // If substitution fails, the default argument is set to a + // RecoveryExpr that wraps the uninstantiated default argument so + // that downstream diagnostics are omitted. + assert(PVD->hasUninstantiatedDefaultArg()); + Expr *UninstExpr = PVD->getUninstantiatedDefaultArg(); + ExprResult ErrorResult = SemaRef.CreateRecoveryExpr( + UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), + { UninstExpr }, UninstExpr->getType()); + if (ErrorResult.isUsable()) + PVD->setDefaultArg(ErrorResult.get()); + } + } + } + SemaRef.CheckFunctionDeclaration(/*Scope*/ nullptr, Function, Previous, IsExplicitSpecialization, Function->isThisDeclarationADefinition()); @@ -2644,6 +2679,40 @@ Previous.clear(); } + // Per [temp.inst], default arguments in member functions of local classes + // are instantiated along with the member function declaration. For example: + // + // template<typename T> + // void ft() { + // struct lc { + // int operator()(int p = []{ return T::value; }()); + // }; + // } + // template void ft<int>(); // error: type 'int' cannot be used prior + // to '::'because it has no members + // + // The error is issued during instantiation of ft<int>()::lc::operator() + // because substitution into the default argument fails; the default argument + // is instantiated even though it is never used. + if (D->isInLocalScopeForInstantiation()) { + for (unsigned P = 0; P < Params.size(); ++P) { + if (!Params[P]->hasDefaultArg()) + continue; + if (SemaRef.SubstDefaultArgument(StartLoc, Params[P], TemplateArgs)) { + // If substitution fails, the default argument is set to a + // RecoveryExpr that wraps the uninstantiated default argument so + // that downstream diagnostics are omitted. + assert(Params[P]->hasUninstantiatedDefaultArg()); + Expr *UninstExpr = Params[P]->getUninstantiatedDefaultArg(); + ExprResult ErrorResult = SemaRef.CreateRecoveryExpr( + UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), + { UninstExpr }, UninstExpr->getType()); + if (ErrorResult.isUsable()) + Params[P]->setDefaultArg(ErrorResult.get()); + } + } + } + SemaRef.CheckFunctionDeclaration(nullptr, Method, Previous, IsExplicitSpecialization, Method->isThisDeclarationADefinition()); @@ -4489,10 +4558,6 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param) { assert(Param->hasUninstantiatedDefaultArg()); - Expr *UninstExpr = Param->getUninstantiatedDefaultArg(); - - EnterExpressionEvaluationContext EvalContext( - *this, ExpressionEvaluationContext::PotentiallyEvaluated, Param); // Instantiate the expression. // @@ -4514,59 +4579,9 @@ MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary=*/true); - InstantiatingTemplate Inst(*this, CallLoc, Param, - TemplateArgs.getInnermost()); - if (Inst.isInvalid()) - return true; - if (Inst.isAlreadyInstantiating()) { - Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD; - Param->setInvalidDecl(); - return true; - } - - ExprResult Result; - { - // C++ [dcl.fct.default]p5: - // The names in the [default argument] expression are bound, and - // the semantic constraints are checked, at the point where the - // default argument expression appears. - ContextRAII SavedContext(*this, FD); - LocalInstantiationScope Local(*this); - - FunctionDecl *Pattern = FD->getTemplateInstantiationPattern( - /*ForDefinition*/ false); - if (addInstantiatedParametersToScope(FD, Pattern, Local, TemplateArgs)) - return true; - - runWithSufficientStackSpace(CallLoc, [&] { - Result = SubstInitializer(UninstExpr, TemplateArgs, - /*DirectInit*/false); - }); - } - if (Result.isInvalid()) - return true; - - // Check the expression as an initializer for the parameter. - InitializedEntity Entity - = InitializedEntity::InitializeParameter(Context, Param); - InitializationKind Kind = InitializationKind::CreateCopy( - Param->getLocation(), - /*FIXME:EqualLoc*/ UninstExpr->getBeginLoc()); - Expr *ResultE = Result.getAs<Expr>(); - - InitializationSequence InitSeq(*this, Entity, Kind, ResultE); - Result = InitSeq.Perform(*this, Entity, Kind, ResultE); - if (Result.isInvalid()) - return true; - - Result = - ActOnFinishFullExpr(Result.getAs<Expr>(), Param->getOuterLocStart(), - /*DiscardedValue*/ false); - if (Result.isInvalid()) + if (SubstDefaultArgument(CallLoc, Param, TemplateArgs, /*ForCallExpr*/ true)) return true; - // Remember the instantiated default argument. - Param->setDefaultArg(Result.getAs<Expr>()); if (ASTMutationListener *L = getASTMutationListener()) L->DefaultArgumentInstantiated(Param); Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -163,11 +163,11 @@ "Outer template not instantiated?"); } - // If this is a friend declaration and it declares an entity at + // If this is a friend or local declaration and it declares an entity at // namespace scope, take arguments from its lexical parent // instead of its semantic parent, unless of course the pattern we're // instantiating actually comes from the file's context! - if (Function->getFriendObjectKind() && + if ((Function->getFriendObjectKind() || Function->isLocalExternDecl()) && Function->getNonTransparentDeclContext()->isFileContext() && (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { Ctx = Function->getLexicalDeclContext(); @@ -1146,7 +1146,31 @@ ExprResult TransformLambdaExpr(LambdaExpr *E) { LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); - return inherited::TransformLambdaExpr(E); + ExprResult Result = inherited::TransformLambdaExpr(E); + if (Result.isInvalid()) + return Result; + + CXXMethodDecl *MD = Result.getAs<LambdaExpr>()->getCallOperator(); + for (ParmVarDecl *PVD : MD->parameters()) { + if (!PVD->hasDefaultArg()) + continue; + assert(PVD->hasUninstantiatedDefaultArg()); + Expr *UninstExpr = PVD->getUninstantiatedDefaultArg(); + // FIXME: Obtain the source location for the '=' token. + SourceLocation EqualLoc = UninstExpr->getBeginLoc(); + if (SemaRef.SubstDefaultArgument(EqualLoc, PVD, TemplateArgs)) { + // If substitution fails, the default argument is set to a + // RecoveryExpr that wraps the uninstantiated default argument so + // that downstream diagnostics are omitted. + ExprResult ErrorResult = SemaRef.CreateRecoveryExpr( + UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), + { UninstExpr }, UninstExpr->getType()); + if (ErrorResult.isUsable()) + PVD->setDefaultArg(ErrorResult.get()); + } + } + + return Result; } ExprResult TransformRequiresExpr(RequiresExpr *E) { @@ -2457,29 +2481,17 @@ NewParm->setUnparsedDefaultArg(); UnparsedDefaultArgInstantiations[OldParm].push_back(NewParm); } else if (Expr *Arg = OldParm->getDefaultArg()) { - FunctionDecl *OwningFunc = cast<FunctionDecl>(OldParm->getDeclContext()); - if (OwningFunc->isInLocalScopeForInstantiation()) { - // Instantiate default arguments for methods of local classes (DR1484) - // and non-defining declarations. - Sema::ContextRAII SavedContext(*this, OwningFunc); - LocalInstantiationScope Local(*this, true); - ExprResult NewArg = SubstExpr(Arg, TemplateArgs); - if (NewArg.isUsable()) { - // It would be nice if we still had this. - SourceLocation EqualLoc = NewArg.get()->getBeginLoc(); - ExprResult Result = - ConvertParamDefaultArgument(NewParm, NewArg.get(), EqualLoc); - if (Result.isInvalid()) - return nullptr; - - SetParamDefaultArgument(NewParm, Result.getAs<Expr>(), EqualLoc); - } - } else { - // FIXME: if we non-lazily instantiated non-dependent default args for - // non-dependent parameter types we could remove a bunch of duplicate - // conversion warnings for such arguments. - NewParm->setUninstantiatedDefaultArg(Arg); - } + // Default arguments cannot be substituted until the declaration context + // for the associated function or lambda capture class is available. + // This is necessary for cases like the following where construction of + // the lambda capture class for the outer lambda is dependent on the + // parameter types but where the default argument is dependent on the + // outer lambda's declaration context. + // template <typename T> + // auto f() { + // return [](T = []{ return T{}; }()) { return 0; }; + // } + NewParm->setUninstantiatedDefaultArg(Arg); } NewParm->setHasInheritedDefaultArg(OldParm->hasInheritedDefaultArg()); @@ -2524,6 +2536,90 @@ Loc, Params, nullptr, ExtParamInfos, ParamTypes, OutParams, ParamInfos); } +/// Substitute the given template arguments into the default argument. +bool Sema::SubstDefaultArgument( + SourceLocation Loc, + ParmVarDecl *Param, + const MultiLevelTemplateArgumentList &TemplateArgs, + bool ForCallExpr) { + assert(Param->hasUninstantiatedDefaultArg()); + + FunctionDecl *FD = cast<FunctionDecl>(Param->getDeclContext()); + Expr *PatternExpr = Param->getUninstantiatedDefaultArg(); + + EnterExpressionEvaluationContext EvalContext( + *this, ExpressionEvaluationContext::PotentiallyEvaluated, Param); + + InstantiatingTemplate Inst(*this, Loc, Param, TemplateArgs.getInnermost()); + if (Inst.isInvalid()) + return true; + if (Inst.isAlreadyInstantiating()) { + Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD; + Param->setInvalidDecl(); + return true; + } + + ExprResult Result; + { + // C++ [dcl.fct.default]p5: + // The names in the [default argument] expression are bound, and + // the semantic constraints are checked, at the point where the + // default argument expression appears. + ContextRAII SavedContext(*this, FD); + std::unique_ptr<LocalInstantiationScope> LIS; + + if (ForCallExpr) { + // When instantiating a default argument due to use in a call expression, + // an instantiation scope that includes the parameters of the callee is + // required to satisfy references from the default argument. For example: + // template<typename T> void f(T a, int = decltype(a)()); + // void g() { f(0); } + LIS = std::make_unique<LocalInstantiationScope>(*this); + FunctionDecl *PatternFD = FD->getTemplateInstantiationPattern( + /*ForDefinition*/ false); + if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs)) + return true; + } + + runWithSufficientStackSpace(Loc, [&] { + Result = SubstInitializer(PatternExpr, TemplateArgs, + /*DirectInit*/false); + }); + } + if (Result.isInvalid()) + return true; + + if (ForCallExpr) { + // Check the expression as an initializer for the parameter. + InitializedEntity Entity + = InitializedEntity::InitializeParameter(Context, Param); + InitializationKind Kind = InitializationKind::CreateCopy( + Param->getLocation(), + /*FIXME:EqualLoc*/ PatternExpr->getBeginLoc()); + Expr *ResultE = Result.getAs<Expr>(); + + InitializationSequence InitSeq(*this, Entity, Kind, ResultE); + Result = InitSeq.Perform(*this, Entity, Kind, ResultE); + if (Result.isInvalid()) + return true; + + Result = + ActOnFinishFullExpr(Result.getAs<Expr>(), Param->getOuterLocStart(), + /*DiscardedValue*/ false); + } else { + // FIXME: Obtain the source location for the '=' token. + SourceLocation EqualLoc = PatternExpr->getBeginLoc(); + Result = ConvertParamDefaultArgument(Param, Result.getAs<Expr>(), EqualLoc); + } + if (Result.isInvalid()) + return true; + + // Remember the instantiated default argument. + Param->setDefaultArg(Result.getAs<Expr>()); + + return false; +} + /// Perform substitution on the base class specifiers of the /// given class template specialization. /// Index: clang/lib/AST/DeclBase.cpp =================================================================== --- clang/lib/AST/DeclBase.cpp +++ clang/lib/AST/DeclBase.cpp @@ -252,12 +252,12 @@ bool Decl::isTemplated() const { // A declaration is templated if it is a template or a template pattern, or - // is within (lexcially for a friend, semantically otherwise) a dependent - // context. - // FIXME: Should local extern declarations be treated like friends? + // is within (lexcially for a friend or local function declaration, + // semantically otherwise) a dependent context. if (auto *AsDC = dyn_cast<DeclContext>(this)) return AsDC->isDependentContext(); - auto *DC = getFriendObjectKind() ? getLexicalDeclContext() : getDeclContext(); + auto *DC = getFriendObjectKind() || isLocalExternDecl() + ? getLexicalDeclContext() : getDeclContext(); return DC->isDependentContext() || isTemplateDecl() || getDescribedTemplateParams(); } Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -9687,6 +9687,9 @@ SmallVectorImpl<QualType> &ParamTypes, SmallVectorImpl<ParmVarDecl *> *OutParams, ExtParameterInfoBuilder &ParamInfos); + bool SubstDefaultArgument(SourceLocation Loc, ParmVarDecl *Param, + const MultiLevelTemplateArgumentList &TemplateArgs, + bool ForCallExpr = false); ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits