From: Douglas Gregor Date: Sat, 18 Feb 2012 05:51:20 +0000 (+0000) Subject: Unify our computation of the type of a captured reference to a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=68932845a432d2a1dbbc57a84fd85bbb37c90487;p=clang Unify our computation of the type of a captured reference to a variable; it was previously duplicated, and one of the copies failed to account for outer non-mutable lambda captures. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150872 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6293be3088..87ec1aadef 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2327,6 +2327,10 @@ public: bool Diagnose, QualType &Type, unsigned &FunctionScopesIndex, bool &Nested); + /// \brief Given a variable, determine the type that a reference to that + /// variable will have in the given scope. + QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc); + /// \brief Determine the type of the field that will capture the /// given variable in a lambda expression. /// diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index c15102c4da..38783eb865 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2094,42 +2094,6 @@ static bool shouldBuildBlockDeclRef(ValueDecl *D, Sema &S) { return S.getCurBlock() != 0; } -/// \brief Determine whether the given lambda would capture the given -/// variable by copy. -static bool willCaptureByCopy(LambdaScopeInfo *LSI, VarDecl *Var) { - if (LSI->isCaptured(Var)) - return LSI->getCapture(Var).isCopyCapture(); - - return LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval; -} - -static bool shouldAddConstQualToVarRef(ValueDecl *D, Sema &S) { - VarDecl *var = dyn_cast(D); - if (!var) - return false; - if (var->getDeclContext() == S.CurContext) - return false; - if (!var->hasLocalStorage()) - return false; - - LambdaScopeInfo *LSI = S.getCurLambda(); - if (!LSI) - return false; - - // We don't actually allow capturing a __block variable in a lambda, but - // this way gives better diagnostics. - if (var->hasAttr()) - return false; - - // FIXME: Does the addition of const really only apply in - // potentially-evaluated contexts? The text in the lambda spec - // about decltype hints that it might apply in unevaluated contexts - // as well... and there's precent in our blocks implementation. - return !LSI->Mutable && - S.ExprEvalContexts.back().Context != Sema::Unevaluated && - willCaptureByCopy(LSI, var); -} - static ExprResult BuildBlockDeclRefExpr(Sema &S, ValueDecl *VD, const DeclarationNameInfo &NameInfo) { VarDecl *var = cast(VD); @@ -2269,7 +2233,7 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS, // fallthrough case Decl::ImplicitParam: - case Decl::ParmVar: + case Decl::ParmVar: { // These are always l-values. valueKind = VK_LValue; type = type.getNonReferenceType(); @@ -2277,11 +2241,18 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS, if (shouldBuildBlockDeclRef(VD, *this)) return BuildBlockDeclRefExpr(*this, VD, NameInfo); - if (shouldAddConstQualToVarRef(VD, *this)) - type.addConst(); - + // FIXME: Does the addition of const really only apply in + // potentially-evaluated contexts? Since the variable isn't actually + // captured in an unevaluated context, it seems that the answer is no. + if (ExprEvalContexts.back().Context != Sema::Unevaluated) { + QualType CapturedType = getCapturedDeclRefType(cast(VD), Loc); + if (!CapturedType.isNull()) + type = CapturedType; + } + break; - + } + case Decl::Function: { const FunctionType *fty = type->castAs(); @@ -9850,6 +9821,34 @@ bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit, return !Type->isVariablyModifiedType(); } +QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) { + QualType T = Var->getType().getNonReferenceType(); + unsigned FunctionScopesIndex; + bool Nested; + // Determine whether we can capture this variable. + if (!canCaptureVariable(Var, Loc, /*Explicit=*/false, /*Diagnose=*/false, + T, FunctionScopesIndex, Nested)) + return QualType(); + + // Outer lambda scopes may have an effect on the type of a + // capture. Walk the captures outside-in to determine + // whether they can add 'const' to a capture by copy. + T = Var->getType().getNonReferenceType(); + if (FunctionScopesIndex == FunctionScopes.size()) + --FunctionScopesIndex; + for (unsigned I = FunctionScopesIndex, E = FunctionScopes.size(); + I != E; ++I) { + CapturingScopeInfo *CSI = dyn_cast(FunctionScopes[I]); + if (!CSI) + break; + + if (shouldAddConstForScope(CSI, Var)) + T.addConst(); + } + + return T; +} + // Check if the variable needs to be captured; if so, try to perform // the capture. void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc, diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index a4b3d86d10..9c8a68b3da 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -4382,42 +4382,9 @@ static QualType getDecltypeForExpr(Sema &S, Expr *E) { if (isa(E)) { if (DeclRefExpr *DRE = dyn_cast(E->IgnoreParens())) { if (VarDecl *Var = dyn_cast(DRE->getDecl())) { - QualType T = Var->getType(); - unsigned FunctionScopesIndex; - bool Nested; - // Determine whether we can capture this variable. - if (S.canCaptureVariable(Var, DRE->getLocation(), - /*Explicit=*/false, /*Diagnose=*/false, - T, FunctionScopesIndex, Nested)) { - // Outer lambda scopes may have an effect on the type of a - // capture. Walk the captures outside-in to determine - // whether they can add 'const' to a capture by copy. - if (FunctionScopesIndex == S.FunctionScopes.size()) - --FunctionScopesIndex; - for (unsigned I = FunctionScopesIndex, - E = S.FunctionScopes.size(); - I != E; ++I) { - LambdaScopeInfo *LSI - = dyn_cast(S.FunctionScopes[I]); - if (!LSI) - continue; - - bool ByRef = false; - if (LSI->isCaptured(Var)) - ByRef = LSI->getCapture(Var).isReferenceCapture(); - else - ByRef = (LSI->ImpCaptureStyle - == CapturingScopeInfo::ImpCap_LambdaByref); - - T = S.getLambdaCaptureFieldType(T, ByRef); - if (!ByRef && !LSI->Mutable) - T.addConst(); - } - - if (!T->isReferenceType()) - T = S.Context.getLValueReferenceType(T); - return T; - } + QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation()); + if (!T.isNull()) + return S.Context.getLValueReferenceType(T); } } } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp index 58fa766352..1723ee39fd 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp @@ -13,4 +13,7 @@ void test_capture(X x) { [x] { // expected-error{{call to deleted constructor of 'const X'}} }(); }(); + + int a; + [=]{ [&] { int&x = a; }(); }(); // expected-error{{binding of reference to type 'int' to a value of type 'const int' drops qualifiers}} } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp index 03cbe32ef4..e9356136c7 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp @@ -17,12 +17,12 @@ void f3() { const int &irc = i; [=,&irc,&ir] { + static_assert(is_same::value, + "should be const float&"); static_assert(is_same::value, "should be float"); static_assert(is_same::value, "should be const float&"); static_assert(is_same::value, "should be float&"); - static_assert(is_same::value, - "should be const float&"); static_assert(is_same::value, "should be int&"); static_assert(is_same::value, "should be int&"); static_assert(is_same::value,