diff options
| author | Faisal Vali <faisalv@yahoo.com> | 2017-02-16 04:12:21 +0000 |
|---|---|---|
| committer | Faisal Vali <faisalv@yahoo.com> | 2017-02-16 04:12:21 +0000 |
| commit | 051e3a2b7d51946de1e1d39c687cba34ef276617 (patch) | |
| tree | fce2648691ba29cf38b2283a08c8a65dc3ea1135 /clang/lib | |
| parent | da5cc8466186b15f31db3c7c98c4f0eee3ad7ced (diff) | |
| download | bcm5719-llvm-051e3a2b7d51946de1e1d39c687cba34ef276617.tar.gz bcm5719-llvm-051e3a2b7d51946de1e1d39c687cba34ef276617.zip | |
[cxx1z-constexpr-lambda] Implement captures - thus completing implementation of constexpr lambdas.
Enable evaluation of captures within constexpr lambdas by using a strategy similar to that used in CodeGen:
- when starting evaluation of a lambda's call operator, create a map from VarDecl's to a closure's FieldDecls
- every time a VarDecl (or '*this) that represents a capture is encountered while evaluating the expression via the expression evaluator (specifically the LValueEvaluator) in ExprConstant.cpp - it is replaced by the corresponding FieldDecl LValue (an Lvalue-to-Rvalue conversion on this LValue representation then determines the right rvalue when needed).
Thanks to Richard Smith and Hubert Tong for their review and feedback!
https://reviews.llvm.org/D29748
llvm-svn: 295279
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 108 |
1 files changed, 101 insertions, 7 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index dff126db0c2..337eaa40b95 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -425,6 +425,17 @@ namespace { /// Index - The call index of this call. unsigned Index; + // FIXME: Adding this to every 'CallStackFrame' may have a nontrivial impact + // on the overall stack usage of deeply-recursing constexpr evaluataions. + // (We should cache this map rather than recomputing it repeatedly.) + // But let's try this and see how it goes; we can look into caching the map + // as a later change. + + /// LambdaCaptureFields - Mapping from captured variables/this to + /// corresponding data members in the closure class. + llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + FieldDecl *LambdaThisCaptureField; + CallStackFrame(EvalInfo &Info, SourceLocation CallLoc, const FunctionDecl *Callee, const LValue *This, APValue *Arguments); @@ -2279,6 +2290,10 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E, return true; } +static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, + QualType Type, const LValue &LVal, + APValue &RVal); + /// Try to evaluate the initializer for a variable declaration. /// /// \param Info Information about the ongoing evaluation. @@ -2290,6 +2305,7 @@ static bool HandleLValueComplexElement(EvalInfo &Info, const Expr *E, static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, const VarDecl *VD, CallStackFrame *Frame, APValue *&Result) { + // If this is a parameter to an active constexpr function call, perform // argument substitution. if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) { @@ -4180,6 +4196,10 @@ static bool HandleFunctionCall(SourceLocation CallLoc, return false; This->moveInto(Result); return true; + } else if (MD && isLambdaCallOperator(MD)) { + // We're in a lambda; determine the lambda capture field maps. + MD->getParent()->getCaptureFields(Frame.LambdaCaptureFields, + Frame.LambdaThisCaptureField); } StmtResult Ret = {Result, ResultSlot}; @@ -5041,6 +5061,33 @@ bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { + + // If we are within a lambda's call operator, check whether the 'VD' referred + // to within 'E' actually represents a lambda-capture that maps to a + // data-member/field within the closure object, and if so, evaluate to the + // field or what the field refers to. + if (Info.CurrentCall && isLambdaCallOperator(Info.CurrentCall->Callee)) { + if (auto *FD = Info.CurrentCall->LambdaCaptureFields.lookup(VD)) { + if (Info.checkingPotentialConstantExpression()) + return false; + // Start with 'Result' referring to the complete closure object... + Result = *Info.CurrentCall->This; + // ... then update it to refer to the field of the closure object + // that represents the capture. + if (!HandleLValueMember(Info, E, Result, FD)) + return false; + // And if the field is of reference type, update 'Result' to refer to what + // the field refers to. + if (FD->getType()->isReferenceType()) { + APValue RVal; + if (!handleLValueToRValueConversion(Info, E, FD->getType(), Result, + RVal)) + return false; + Result.setFrom(Info.Ctx, RVal); + } + return true; + } + } CallStackFrame *Frame = nullptr; if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1) { // Only if a local variable was declared in the function currently being @@ -5440,6 +5487,27 @@ public: return false; } Result = *Info.CurrentCall->This; + // If we are inside a lambda's call operator, the 'this' expression refers + // to the enclosing '*this' object (either by value or reference) which is + // either copied into the closure object's field that represents the '*this' + // or refers to '*this'. + if (isLambdaCallOperator(Info.CurrentCall->Callee)) { + // Update 'Result' to refer to the data member/field of the closure object + // that represents the '*this' capture. + if (!HandleLValueMember(Info, E, Result, + Info.CurrentCall->LambdaThisCaptureField)) + return false; + // If we captured '*this' by reference, replace the field with its referent. + if (Info.CurrentCall->LambdaThisCaptureField->getType() + ->isPointerType()) { + APValue RVal; + if (!handleLValueToRValueConversion(Info, E, E->getType(), Result, + RVal)) + return false; + + Result.setFrom(Info.Ctx, RVal); + } + } return true; } @@ -6269,14 +6337,40 @@ bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) { if (ClosureClass->isInvalidDecl()) return false; if (Info.checkingPotentialConstantExpression()) return true; - if (E->capture_size()) { - Info.FFDiag(E, diag::note_unimplemented_constexpr_lambda_feature_ast) - << "can not evaluate lambda expressions with captures"; - return false; + + const size_t NumFields = + std::distance(ClosureClass->field_begin(), ClosureClass->field_end()); + + assert(NumFields == + std::distance(E->capture_init_begin(), E->capture_init_end()) && + "The number of lambda capture initializers should equal the number of " + "fields within the closure type"); + + Result = APValue(APValue::UninitStruct(), /*NumBases*/0, NumFields); + // Iterate through all the lambda's closure object's fields and initialize + // them. + auto *CaptureInitIt = E->capture_init_begin(); + const LambdaCapture *CaptureIt = ClosureClass->captures_begin(); + bool Success = true; + for (const auto *Field : ClosureClass->fields()) { + assert(CaptureInitIt != E->capture_init_end()); + // Get the initializer for this field + Expr *const CurFieldInit = *CaptureInitIt++; + + // If there is no initializer, either this is a VLA or an error has + // occurred. + if (!CurFieldInit) + return Error(E); + + APValue &FieldVal = Result.getStructField(Field->getFieldIndex()); + if (!EvaluateInPlace(FieldVal, Info, This, CurFieldInit)) { + if (!Info.keepEvaluatingAfterFailure()) + return false; + Success = false; + } + ++CaptureIt; } - // FIXME: Implement captures. - Result = APValue(APValue::UninitStruct(), /*NumBases*/0, /*NumFields*/0); - return true; + return Success; } static bool EvaluateRecord(const Expr *E, const LValue &This, |

