aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorFaisal Vali <faisalv@yahoo.com>2013-10-03 06:29:33 +0000
committerFaisal Vali <faisalv@yahoo.com>2013-10-03 06:29:33 +0000
commit618c28547a7f7cc785a6c6301f79febf5a584f9e (patch)
treef9d971d1b4ea06cabdf6af801512ae21fb0074f2 /lib
parent11f22a35b7f08a8d017f6ab26e440edffc930f96 (diff)
downloadclang-618c28547a7f7cc785a6c6301f79febf5a584f9e.tar.gz
Teach TreeTransform and family how to transform generic lambdas within templates and nested within themselves.
This does not yet include capturing (that is next). Please see test file for examples. This patch was LGTM'd by Doug: http://llvm-reviews.chandlerc.com/D1784 http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130930/090048.html When I first committed this patch - a bunch of buildbots were unable to compile the code that VS2010 seemed to compile. Seems like there was a dependency on Sema/Template.h which VS did not seem to need, but I have now added for the other compilers. It still compiles on Visual Studio 2010 - lets hope the buildbots remain quiet (please!) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191879 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r--lib/Sema/SemaTemplateInstantiate.cpp61
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp24
-rw-r--r--lib/Sema/TreeTransform.h96
3 files changed, 153 insertions, 28 deletions
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 32111bac3a..29481d6c63 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -14,6 +14,7 @@
#include "TreeTransform.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/LangOptions.h"
@@ -130,6 +131,11 @@ Sema::getTemplateInstantiationArgs(NamedDecl *D,
assert(Function->getPrimaryTemplate() && "No function template?");
if (Function->getPrimaryTemplate()->isMemberSpecialization())
break;
+
+ // If this function is a generic lambda specialization, we are done.
+ if (isGenericLambdaCallOperatorSpecialization(Function))
+ break;
+
} else if (FunctionTemplateDecl *FunTmpl
= Function->getDescribedFunctionTemplate()) {
// Add the "injected" template arguments.
@@ -911,13 +917,56 @@ namespace {
}
ExprResult TransformLambdaScope(LambdaExpr *E,
- CXXMethodDecl *CallOperator) {
- CallOperator->setInstantiationOfMemberFunction(E->getCallOperator(),
- TSK_ImplicitInstantiation);
- return TreeTransform<TemplateInstantiator>::
- TransformLambdaScope(E, CallOperator);
+ CXXMethodDecl *NewCallOperator) {
+ // If a lambda is undergoing transformation for instance in the
+ // call to foo('a') below:
+ // template<class T> void foo(T t) {
+ // auto L1 = [](T a) { return a; };
+ // auto L2 = [](char b) { return b; };
+ // auto L3 = [](auto c) { return c; };
+ // }
+ // The AST nodes of the OldCallOperators within the primary template foo
+ // are connected to the NewCallOperators within the specialization of foo.
+ // - In the case of L1 and L2 we set the NewCallOperator to be considered
+ // an instantiation of the OldCallOperator.
+ // - In the generic lambda case, we set the NewTemplate to be considered
+ // an "instantiation" of the OldTemplate.
+ // See the documentation and use of get/setInstantiationOfMemberFunction
+ // and get/setInstantiatedFromMemberTemplate to appreciate the relevance
+ // of creating these links.
+ // And so it goes on and on with nested generic lambdas.
+ CXXMethodDecl *const OldCallOperator = E->getCallOperator();
+ FunctionTemplateDecl *const NewCallOperatorTemplate =
+ NewCallOperator->getDescribedFunctionTemplate();
+ FunctionTemplateDecl *const OldCallOperatorTemplate =
+ OldCallOperator->getDescribedFunctionTemplate();
+
+ if (!NewCallOperatorTemplate)
+ NewCallOperator->setInstantiationOfMemberFunction(OldCallOperator,
+ TSK_ImplicitInstantiation);
+ else {
+ NewCallOperatorTemplate->setInstantiatedFromMemberTemplate(
+ OldCallOperatorTemplate);
+ // Set this as a specialization so we don't go digging into the
+ // OldCallOperatorTemplate when retrieving the
+ // 'FunctionDecl::getTemplateInstantiationPattern()'
+ NewCallOperatorTemplate->setMemberSpecialization();
+ }
+ return inherited::TransformLambdaScope(E, NewCallOperator);
+ }
+ TemplateParameterList *TransformTemplateParameterList(
+ TemplateParameterList *OrigTPL) {
+ TemplateParameterList *NewTPL = 0;
+ if (OrigTPL) {
+ if (!OrigTPL->size()) return OrigTPL; // size 0, do nothing
+
+ DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
+ TemplateDeclInstantiator DeclInstantiator(getSema(),
+ /* DeclContext *Owner */ Owner, TemplateArgs);
+ NewTPL = DeclInstantiator.SubstTemplateParams(OrigTPL);
+ }
+ return NewTPL;
}
-
private:
ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm,
SourceLocation loc,
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 35f3616db6..359fb73166 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4171,6 +4171,30 @@ DeclContext *Sema::FindInstantiatedContext(SourceLocation Loc, DeclContext* DC,
NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs) {
DeclContext *ParentDC = D->getDeclContext();
+
+ // If we have a parameter from a non-dependent context with a non-dependent
+ // type it obviously can not be mapped to a different instantiated decl.
+ // Consider the code below, with explicit return types, when N gets
+ // specialized ...:
+ // template<class T> void fooT(T t) {
+ // auto L = [](auto a) -> void {
+ // auto M = [](char b) -> void {
+ // auto N = [](auto c) -> void {
+ // int x = sizeof(a) + sizeof(b) +
+ // sizeof(c);
+ // };
+ // N('a');
+ // };
+ // };
+ // L(3.14);
+ // }
+ // fooT('a');
+ // ... without this check below, findInstantiationOf fails with
+ // an assertion violation.
+ if (isa<ParmVarDecl>(D) && !ParentDC->isDependentContext() &&
+ !cast<ParmVarDecl>(D)->getType()->isInstantiationDependentType())
+ return D;
+
if (isa<ParmVarDecl>(D) || isa<NonTypeTemplateParmDecl>(D) ||
isa<TemplateTypeParmDecl>(D) || isa<TemplateTemplateParmDecl>(D) ||
(ParentDC->isFunctionOrMethod() && ParentDC->isDependentContext()) ||
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 977d0132b7..7c343635a7 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -33,6 +33,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Sema/SemaInternal.h"
+#include "clang/Sema/Template.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
@@ -594,6 +595,11 @@ public:
/// \brief Transform the captures and body of a lambda expression.
ExprResult TransformLambdaScope(LambdaExpr *E, CXXMethodDecl *CallOperator);
+ TemplateParameterList *TransformTemplateParameterList(
+ TemplateParameterList *TPL) {
+ return TPL;
+ }
+
ExprResult TransformAddressOfOperand(Expr *E);
ExprResult TransformDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E,
bool IsAddressOfOperand);
@@ -4573,6 +4579,19 @@ template<typename Derived>
QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
DecltypeTypeLoc TL) {
const DecltypeType *T = TL.getTypePtr();
+ // Don't transform a decltype construct that has already been transformed
+ // into a non-dependent type.
+ // Allows the following to compile:
+ // auto L = [](auto a) {
+ // return [](auto b) ->decltype(a) {
+ // return b;
+ // };
+ //};
+ if (!T->isInstantiationDependentType()) {
+ DecltypeTypeLoc NewTL = TLB.push<DecltypeTypeLoc>(TL.getType());
+ NewTL.setNameLoc(TL.getNameLoc());
+ return NewTL.getType();
+ }
// decltype expressions are not potentially evaluated contexts
EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated, 0,
@@ -8284,24 +8303,27 @@ template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
- // FIXME: Implement nested generic lambda transformations.
- if (E->isGenericLambda()) {
- getSema().Diag(E->getIntroducerRange().getBegin(),
- diag::err_glambda_not_fully_implemented)
- << " template transformation of generic lambdas not implemented yet";
- return ExprError();
- }
- // Transform the type of the lambda parameters and start the definition of
- // the lambda itself.
- TypeSourceInfo *MethodTy
- = TransformType(E->getCallOperator()->getTypeSourceInfo());
- if (!MethodTy)
+ getSema().PushLambdaScope();
+ LambdaScopeInfo *LSI = getSema().getCurLambda();
+ TemplateParameterList *const OrigTPL = E->getTemplateParameterList();
+ TemplateParameterList *NewTPL = 0;
+ // Transform the template parameters, and add them to the
+ // current instantiation scope.
+ if (OrigTPL) {
+ NewTPL = getDerived().TransformTemplateParameterList(OrigTPL);
+ }
+ LSI->GLTemplateParameterList = NewTPL;
+ // Transform the type of the lambda parameters and start the definition of
+ // the lambda itself.
+ TypeSourceInfo *OldCallOpTSI = E->getCallOperator()->getTypeSourceInfo();
+ TypeSourceInfo *NewCallOpTSI = TransformType(OldCallOpTSI);
+ if (!NewCallOpTSI)
return ExprError();
// Create the local class that will describe the lambda.
CXXRecordDecl *Class
= getSema().createLambdaClosureType(E->getIntroducerRange(),
- MethodTy,
+ NewCallOpTSI,
/*KnownDependent=*/false);
getDerived().transformedLocalDecl(E->getLambdaClass(), Class);
@@ -8313,19 +8335,49 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->getCallOperator()->param_size(),
0, ParamTypes, &Params))
return ExprError();
- getSema().PushLambdaScope();
- LambdaScopeInfo *LSI = getSema().getCurLambda();
- // TODO: Fix for nested lambdas
- LSI->GLTemplateParameterList = 0;
+
// Build the call operator.
- CXXMethodDecl *CallOperator
+ CXXMethodDecl *NewCallOperator
= getSema().startLambdaDefinition(Class, E->getIntroducerRange(),
- MethodTy,
+ NewCallOpTSI,
E->getCallOperator()->getLocEnd(),
Params);
- getDerived().transformAttrs(E->getCallOperator(), CallOperator);
-
- return getDerived().TransformLambdaScope(E, CallOperator);
+ LSI->CallOperator = NewCallOperator;
+ // Fix the Decl Contexts of the parameters within the call op function
+ // prototype.
+ getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
+
+ TypeLoc NewCallOpTL = NewCallOpTSI->getTypeLoc();
+ FunctionProtoTypeLoc NewFPTL = NewCallOpTL.castAs<FunctionProtoTypeLoc>();
+ ParmVarDecl **NewParamDeclArray = NewFPTL.getParmArray();
+ const unsigned NewNumArgs = NewFPTL.getNumArgs();
+ for (unsigned I = 0; I < NewNumArgs; ++I) {
+ NewParamDeclArray[I]->setOwningFunction(NewCallOperator);
+ }
+ // If this is a non-generic lambda, the parameters do not get added to the
+ // current instantiation scope, so add them. This feels kludgey.
+ // Anyway, it allows the following to compile when the enclosing template
+ // is specialized and the entire lambda expression has to be
+ // transformed. Without this FindInstantiatedDecl causes an assertion.
+ // template<class T> void foo(T t) {
+ // auto L = [](auto a) {
+ // auto M = [](char b) { <-- note: non-generic lambda
+ // auto N = [](auto c) {
+ // int x = sizeof(a);
+ // x = sizeof(b); <-- specifically this line
+ // x = sizeof(c);
+ // };
+ // };
+ // };
+ // }
+ // foo('a');
+ //
+ if (!E->isGenericLambda()) {
+ for (unsigned I = 0; I < NewNumArgs; ++I)
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(
+ NewParamDeclArray[I], NewParamDeclArray[I]);
+ }
+ return getDerived().TransformLambdaScope(E, NewCallOperator);
}
template<typename Derived>