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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits