Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,7 @@ Bug Fixes to C++ Support
- Fixed recognition of ``std::initializer_list`` when it's surrounded with ``extern "C++"`` and exported
out of a module (which is the case e.g. in MSVC's implementation of ``std`` module). (#GH118218)
- Fixed a pack expansion issue in checking unexpanded parameter sizes. (#GH17042)
- Fixed a bug where captured structured bindings were modifiable inside non-mutable lambda (#GH95081)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
25 changes: 16 additions & 9 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3352,6 +3352,7 @@ ExprResult Sema::BuildDeclarationNameExpr(
case Decl::VarTemplateSpecialization:
case Decl::VarTemplatePartialSpecialization:
case Decl::Decomposition:
case Decl::Binding:
case Decl::OMPCapturedExpr:
// In C, "extern void blah;" is valid and is an r-value.
if (!getLangOpts().CPlusPlus && !type.hasQualifiers() &&
Expand All @@ -3371,20 +3372,13 @@ ExprResult Sema::BuildDeclarationNameExpr(
// potentially-evaluated contexts? Since the variable isn't actually
// captured in an unevaluated context, it seems that the answer is no.
if (!isUnevaluatedContext()) {
QualType CapturedType = getCapturedDeclRefType(cast<VarDecl>(VD), Loc);
QualType CapturedType = getCapturedDeclRefType(cast<ValueDecl>(VD), Loc);
if (!CapturedType.isNull())
type = CapturedType;
}

break;
}

case Decl::Binding:
// These are always lvalues.
valueKind = VK_LValue;
type = type.getNonReferenceType();
break;

case Decl::Function: {
if (unsigned BID = cast<FunctionDecl>(VD)->getBuiltinID()) {
if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
Expand Down Expand Up @@ -13299,7 +13293,18 @@ static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) {

// The declaration must be a variable which is not declared 'const'.
VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl());
if (!var) return NCCK_None;
if (!var) {
// Bindings also can be captured by lambda in C++
BindingDecl *binding = dyn_cast<BindingDecl>(DRE->getDecl());
if (!binding || binding->getType().isConstQualified())
return NCCK_None;

assert(S.getLangOpts().CPlusPlus && "BindingDecl outside of C++?");
assert(!isa<BlockDecl>(binding->getDeclContext()));

return NCCK_Lambda;
}

if (var->getType().isConstQualified()) return NCCK_None;
assert(var->hasLocalStorage() && "capture added 'const' to non-local?");

Expand Down Expand Up @@ -19247,6 +19252,8 @@ bool Sema::NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc) {
}

QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) {
assert(Var && "Null value cannot be captured");

QualType CaptureType;
QualType DeclRefType;

Expand Down
23 changes: 23 additions & 0 deletions clang/test/SemaCXX/cxx20-decomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,26 @@ namespace ODRUseTests {
}(0); }(0); // expected-note 2{{in instantiation}}
}
}


namespace GH95081 {
void prevent_assignment_check() {
int arr[] = {1,2};
auto [e1, e2] = arr;

auto lambda = [e1] {
e1 = 42; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
};
}

void f(int&) = delete;
void f(const int&);

int arr[1];
void foo() {
auto [x] = arr;
[x]() {
f(x); // deleted f(int&) used to be picked up erroneously
} ();
}
}
Loading