ahatanak updated this revision to Diff 72356. ahatanak added a comment. I agree that extending the logic of getTemplateInstantiationArgs seems like a better approach. I changed Sema::getTemplateInstantiationArgs to search for the template arguments twice, first for the initializer's template arguments and then for the VarTemplateSpecializationDecl. This should enable handling cases where a variable template is declared inside a class/struct.
I wonder whether VarTemplateSpecializationDecl can be made a subclass of DeclContext and have it contain the lambda class of the initializer. I think that will simplify the logic of getTemplateInstantiationArgs. https://reviews.llvm.org/D23096 Files: include/clang/Sema/Sema.h lib/Sema/Sema.cpp lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateInstantiate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/SemaCXX/vartemplate-lambda.cpp test/SemaTemplate/default-expr-arguments-3.cpp
Index: test/SemaTemplate/default-expr-arguments-3.cpp =================================================================== --- /dev/null +++ test/SemaTemplate/default-expr-arguments-3.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -std=c++14 -ast-dump %s 2>&1 | FileCheck %s + +namespace PR28795 { + // CHECK: FunctionDecl{{.*}}func 'void (void)' + // CHECK: LambdaExpr + // CHECK: CXXMethodDecl{{.*}}operator() 'enum foo (enum foo) const' inline + // CHECK: ParmVarDecl{{.*}}f 'enum foo' cinit + // CHECK: DeclRefExpr{{.*}}'enum foo' EnumConstant{{.*}}'a' 'enum foo' + + template<typename T> + void func() { + enum class foo { a, b }; + auto bar = [](foo f = foo::a) { return f; }; + bar(); + } + + void foo() { + func<int>(); + } +} + +// Template struct case: + +// CHECK: ClassTemplateSpecializationDecl{{.*}}struct class2 definition +// CHECK: LambdaExpr +// CHECK: CXXMethodDecl{{.*}}used operator() 'enum foo (enum foo) const' inline +// CHECK: ParmVarDecl{{.*}}f 'enum foo' cinit +// CHECK: DeclRefExpr{{.*}}'enum foo' EnumConstant{{.*}}'a' 'enum foo' + +template <class T> struct class2 { + void bar() { + enum class foo { a, b }; + [](foo f = foo::a) { return f; }(); + } +}; + +template struct class2<int>; + +// CHECK: FunctionDecl{{.*}}f1 'void (void)' +// CHECK: CXXMethodDecl{{.*}}g1 'int (enum foo)' +// CHECK: ParmVarDecl{{.*}}n 'enum foo' cinit +// CHECK: DeclRefExpr{{.*}}'enum foo' EnumConstant{{.*}}'a' 'enum foo' + +template<typename T> +void f1() { + enum class foo { a, b }; + struct S { + int g1(foo n = foo::a); + }; +} + +template void f1<int>(); Index: test/SemaCXX/vartemplate-lambda.cpp =================================================================== --- test/SemaCXX/vartemplate-lambda.cpp +++ test/SemaCXX/vartemplate-lambda.cpp @@ -1,15 +1,22 @@ // RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s -// expected-no-diagnostics template <class> auto fn0 = [] {}; template <typename> void foo0() { fn0<char>(); } template<typename T> auto fn1 = [](auto a) { return a + T(1); }; +template<typename T> auto v1 = [](int a = T(1)) { return a; }(); + +struct S { + template<class T> + static constexpr T t = [](int f = T(7)){return f;}(); // expected-error{{constexpr variable 't<int>' must be initialized by a constant expression}} expected-error{{a lambda expression may not appear inside of a constant expression}} expected-note{{cannot be used in a constant expression}} +}; template <typename X> int foo2() { X a = 0x61; fn1<char>(a); + (void)v1<int>; + (void)S::t<int>; // expected-note{{in instantiation of static data member 'S::t<int>' requested here}} return 0; } Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3932,6 +3932,7 @@ { ContextRAII SwitchContext(*this, Var->getDeclContext()); + VarTemplateSpecializationRAII V(*this, Var); Init = SubstInitializer(OldVar->getInit(), TemplateArgs, OldVar->getInitStyle() == VarDecl::CallInit); } Index: lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiate.cpp +++ lib/Sema/SemaTemplateInstantiate.cpp @@ -33,34 +33,10 @@ // Template Instantiation Support //===----------------------------------------------------------------------===/ -/// \brief Retrieve the template argument list(s) that should be used to -/// instantiate the definition of the given declaration. -/// -/// \param D the declaration for which we are computing template instantiation -/// arguments. -/// -/// \param Innermost if non-NULL, the innermost template argument list. -/// -/// \param RelativeToPrimary true if we should get the template -/// arguments relative to the primary template, even when we're -/// dealing with a specialization. This is only relevant for function -/// template specializations. -/// -/// \param Pattern If non-NULL, indicates the pattern from which we will be -/// instantiating the definition of the given declaration, \p D. This is -/// used to determine the proper set of template instantiation arguments for -/// friend function template specializations. -MultiLevelTemplateArgumentList -Sema::getTemplateInstantiationArgs(NamedDecl *D, - const TemplateArgumentList *Innermost, - bool RelativeToPrimary, - const FunctionDecl *Pattern) { - // Accumulate the set of template argument lists in this structure. - MultiLevelTemplateArgumentList Result; - - if (Innermost) - Result.addOuterTemplateArguments(Innermost); - +static void +getTemplateInstantiationArgs(Sema &S, NamedDecl *D, bool RelativeToPrimary, + const FunctionDecl *Pattern, + MultiLevelTemplateArgumentList &Result) { DeclContext *Ctx = dyn_cast<DeclContext>(D); if (!Ctx) { Ctx = D->getDeclContext(); @@ -71,7 +47,7 @@ // We're done when we hit an explicit specialization. if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && !isa<VarTemplatePartialSpecializationDecl>(Spec)) - return Result; + return; Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); @@ -84,11 +60,11 @@ if (VarTemplatePartialSpecializationDecl *Partial = Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) { if (Partial->isMemberSpecialization()) - return Result; + return; } else { VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>(); if (Tmpl->isMemberSpecialization()) - return Result; + return; } } @@ -103,7 +79,7 @@ = dyn_cast<TemplateTemplateParmDecl>(D)) { for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) Result.addOuterTemplateArguments(None); - return Result; + return; } } } @@ -165,11 +141,21 @@ RelativeToPrimary = false; continue; } + + // Break if this function is a lambda call operator used to initialize + // a variable template specialization. The template arguments of the + // variable template specialization will be added later in + // Sema::getTemplateInstantiationArgs. + if (isLambdaCallOperator(Function)) + if (auto *VT = S.getVarTemplateSpecDecl()) + if (VT->getDeclContext() == + cast<CXXMethodDecl>(Function)->getParent()->getDeclContext()) + break; } else if (CXXRecordDecl *Rec = dyn_cast<CXXRecordDecl>(Ctx)) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { QualType T = ClassTemplate->getInjectedClassNameSpecialization(); const TemplateSpecializationType *TST = - cast<TemplateSpecializationType>(Context.getCanonicalType(T)); + cast<TemplateSpecializationType>(S.Context.getCanonicalType(T)); Result.addOuterTemplateArguments( llvm::makeArrayRef(TST->getArgs(), TST->getNumArgs())); if (ClassTemplate->isMemberSpecialization()) @@ -180,6 +166,43 @@ Ctx = Ctx->getParent(); RelativeToPrimary = false; } +} + +/// \brief Retrieve the template argument list(s) that should be used to +/// instantiate the definition of the given declaration. +/// +/// \param D the declaration for which we are computing template instantiation +/// arguments. +/// +/// \param Innermost if non-NULL, the innermost template argument list. +/// +/// \param RelativeToPrimary true if we should get the template +/// arguments relative to the primary template, even when we're +/// dealing with a specialization. This is only relevant for function +/// template specializations. +/// +/// \param Pattern If non-NULL, indicates the pattern from which we will be +/// instantiating the definition of the given declaration, \p D. This is +/// used to determine the proper set of template instantiation arguments for +/// friend function template specializations. +MultiLevelTemplateArgumentList +Sema::getTemplateInstantiationArgs(NamedDecl *D, + const TemplateArgumentList *Innermost, + bool RelativeToPrimary, + const FunctionDecl *Pattern) { + // Accumulate the set of template argument lists in this structure. + MultiLevelTemplateArgumentList Result; + + if (Innermost) + Result.addOuterTemplateArguments(Innermost); + + ::getTemplateInstantiationArgs(*this, D, RelativeToPrimary, Pattern, Result); + + // If we are instantiating the initializer of a variable template, add the + // template argument lists of the variable template specialization. + if ((D = getVarTemplateSpecDecl())) + ::getTemplateInstantiationArgs(*this, D, RelativeToPrimary, Pattern, + Result); return Result; } @@ -1685,7 +1708,7 @@ // Instantiate default arguments for methods of local classes (DR1484) // and non-defining declarations. Sema::ContextRAII SavedContext(*this, OwningFunc); - LocalInstantiationScope Local(*this); + LocalInstantiationScope Local(*this, true); ExprResult NewArg = SubstExpr(Arg, TemplateArgs); if (NewArg.isUsable()) { // It would be nice if we still had this. Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -429,7 +429,12 @@ bool MightBeCxx11UnevalField = getLangOpts().CPlusPlus11 && isUnevaluatedContext(); - if (!MightBeCxx11UnevalField && !isAddressOfOperand && + // Check if the nested name specifier is an enum type. + bool IsEnum = false; + if (NestedNameSpecifier *NNS = SS.getScopeRep()) + IsEnum = dyn_cast_or_null<EnumType>(NNS->getAsType()); + + if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum && isa<CXXMethodDecl>(DC) && cast<CXXMethodDecl>(DC)->isInstance()) { QualType ThisType = cast<CXXMethodDecl>(DC)->getThisType(Context); Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -74,7 +74,8 @@ TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter) : ExternalSource(nullptr), - isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()), + isMultiplexExternalSource(false), VarTemplateSpec(nullptr), + FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), CollectStats(false), CodeCompleter(CodeCompleter), Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -288,6 +288,8 @@ } bool shouldLinkPossiblyHiddenDecl(LookupResult &Old, const NamedDecl *New); + VarTemplateSpecializationDecl *VarTemplateSpec; + public: typedef OpaquePtr<DeclGroupRef> DeclGroupPtrTy; typedef OpaquePtr<TemplateName> TemplateTy; @@ -665,6 +667,17 @@ } }; + class VarTemplateSpecializationRAII { + Sema &S; + public: + VarTemplateSpecializationRAII(Sema &S, VarDecl *Var) : S(S) { + S.setVarTemplateSpecDecl(dyn_cast<VarTemplateSpecializationDecl>(Var)); + } + ~VarTemplateSpecializationRAII() { + S.clearVarTemplateSpecDecl(); + } + }; + /// \brief RAII object to handle the state changes required to synthesize /// a function body. class SynthesizedFunctionScope { @@ -3689,6 +3702,18 @@ void DiscardCleanupsInEvaluationContext(); + void setVarTemplateSpecDecl(VarTemplateSpecializationDecl *V) { + VarTemplateSpec = V; + } + + void clearVarTemplateSpecDecl() { + VarTemplateSpec = nullptr; + } + + VarTemplateSpecializationDecl *getVarTemplateSpecDecl() { + return VarTemplateSpec; + } + ExprResult TransformToPotentiallyEvaluated(Expr *E); ExprResult HandleExprEvaluationContextForTypeof(Expr *E);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits