/// or within a data member initializer.
LazyDeclPtr ContextDecl;
- /// The list of captures, both explicit and implicit, for this
- /// lambda.
- Capture *Captures = nullptr;
+ /// The lists of captures, both explicit and implicit, for this
+ /// lambda. One list is provided for each merged copy of the lambda.
+ /// The first list corresponds to the canonical definition.
+ /// The destructor is registered by AddCaptureList when necessary.
+ llvm::TinyPtrVector<Capture*> Captures;
/// The type of the call method.
TypeSourceInfo *MethodTyInfo;
Aggregate = false;
PlainOldData = false;
}
+
+ // Add a list of captures.
+ void AddCaptureList(ASTContext &Ctx, Capture *CaptureList);
};
struct DefinitionData *dataPtr() const {
///
/// \note No entries will be added for init-captures, as they do not capture
/// variables.
+ ///
+ /// \note If multiple versions of the lambda are merged together, they may
+ /// have different variable declarations corresponding to the same capture.
+ /// In that case, all of those variable declarations will be added to the
+ /// Captures list, so it may have more than one variable listed per field.
void
getCaptureFields(llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures,
FieldDecl *&ThisCapture) const;
}
capture_const_iterator captures_begin() const {
- return isLambda() ? getLambdaData().Captures : nullptr;
+ if (!isLambda()) return nullptr;
+ LambdaDefinitionData &LambdaData = getLambdaData();
+ return LambdaData.Captures.empty() ? nullptr : LambdaData.Captures.front();
}
capture_const_iterator captures_end() const {
}
}
+void CXXRecordDecl::LambdaDefinitionData::AddCaptureList(ASTContext &Ctx,
+ Capture *CaptureList) {
+ Captures.push_back(CaptureList);
+ if (Captures.size() == 2) {
+ // The TinyPtrVector member now needs destruction.
+ Ctx.addDestruction(&Captures);
+ }
+}
+
void CXXRecordDecl::setCaptures(ASTContext &Context,
ArrayRef<LambdaCapture> Captures) {
CXXRecordDecl::LambdaDefinitionData &Data = getLambdaData();
// Copy captures.
Data.NumCaptures = Captures.size();
Data.NumExplicitCaptures = 0;
- Data.Captures = (LambdaCapture *)Context.Allocate(sizeof(LambdaCapture) *
- Captures.size());
- LambdaCapture *ToCapture = Data.Captures;
+ auto *ToCapture = (LambdaCapture *)Context.Allocate(sizeof(LambdaCapture) *
+ Captures.size());
+ Data.AddCaptureList(Context, ToCapture);
for (unsigned I = 0, N = Captures.size(); I != N; ++I) {
if (Captures[I].isExplicit())
++Data.NumExplicitCaptures;
ThisCapture = nullptr;
LambdaDefinitionData &Lambda = getLambdaData();
- RecordDecl::field_iterator Field = field_begin();
- for (const LambdaCapture *C = Lambda.Captures, *CEnd = C + Lambda.NumCaptures;
- C != CEnd; ++C, ++Field) {
- if (C->capturesThis())
- ThisCapture = *Field;
- else if (C->capturesVariable())
- Captures[C->getCapturedVar()] = *Field;
+ for (const LambdaCapture *List : Lambda.Captures) {
+ RecordDecl::field_iterator Field = field_begin();
+ for (const LambdaCapture *C = List, *CEnd = C + Lambda.NumCaptures;
+ C != CEnd; ++C, ++Field) {
+ if (C->capturesThis())
+ ThisCapture = *Field;
+ else if (C->capturesVariable())
+ Captures[C->getCapturedVar()] = *Field;
+ }
+ assert(Field == field_end());
}
- assert(Field == field_end());
}
TemplateParameterList *
}
LambdaExpr::capture_iterator LambdaExpr::capture_begin() const {
- return getLambdaClass()->getLambdaData().Captures;
+ return getLambdaClass()->captures_begin();
}
LambdaExpr::capture_iterator LambdaExpr::capture_end() const {
- return capture_begin() + capture_size();
+ return getLambdaClass()->captures_end();
}
LambdaExpr::capture_range LambdaExpr::captures() const {
}
LambdaExpr::capture_iterator LambdaExpr::explicit_capture_end() const {
- struct CXXRecordDecl::LambdaDefinitionData &Data
- = getLambdaClass()->getLambdaData();
- return Data.Captures + Data.NumExplicitCaptures;
+ return capture_begin() +
+ getLambdaClass()->getLambdaData().NumExplicitCaptures;
}
LambdaExpr::capture_range LambdaExpr::explicit_captures() const {
Lambda.ManglingNumber = Record.readInt();
D->setDeviceLambdaManglingNumber(Record.readInt());
Lambda.ContextDecl = readDeclID();
- Lambda.Captures = (Capture *)Reader.getContext().Allocate(
- sizeof(Capture) * Lambda.NumCaptures);
- Capture *ToCapture = Lambda.Captures;
+ Capture *ToCapture = nullptr;
+ if (Lambda.NumCaptures) {
+ ToCapture = (Capture *)Reader.getContext().Allocate(sizeof(Capture) *
+ Lambda.NumCaptures);
+ Lambda.AddCaptureList(Reader.getContext(), ToCapture);
+ }
Lambda.MethodTyInfo = readTypeSourceInfo();
for (unsigned I = 0, N = Lambda.NumCaptures; I != N; ++I) {
SourceLocation Loc = readSourceLocation();
// lazily load it.
if (DD.IsLambda) {
- // FIXME: ODR-checking for merging lambdas (this happens, for instance,
- // when they occur within the body of a function template specialization).
+ auto &Lambda1 = static_cast<CXXRecordDecl::LambdaDefinitionData &>(DD);
+ auto &Lambda2 = static_cast<CXXRecordDecl::LambdaDefinitionData &>(MergeDD);
+ DetectedOdrViolation |= Lambda1.DependencyKind != Lambda2.DependencyKind;
+ DetectedOdrViolation |= Lambda1.IsGenericLambda != Lambda2.IsGenericLambda;
+ DetectedOdrViolation |= Lambda1.CaptureDefault != Lambda2.CaptureDefault;
+ DetectedOdrViolation |= Lambda1.NumCaptures != Lambda2.NumCaptures;
+ DetectedOdrViolation |=
+ Lambda1.NumExplicitCaptures != Lambda2.NumExplicitCaptures;
+ DetectedOdrViolation |=
+ Lambda1.HasKnownInternalLinkage != Lambda2.HasKnownInternalLinkage;
+ DetectedOdrViolation |= Lambda1.ManglingNumber != Lambda2.ManglingNumber;
+
+ if (Lambda1.NumCaptures && Lambda1.NumCaptures == Lambda2.NumCaptures) {
+ for (unsigned I = 0, N = Lambda1.NumCaptures; I != N; ++I) {
+ LambdaCapture &Cap1 = Lambda1.Captures.front()[I];
+ LambdaCapture &Cap2 = Lambda2.Captures.front()[I];
+ DetectedOdrViolation |= Cap1.getCaptureKind() != Cap2.getCaptureKind();
+ }
+ Lambda1.AddCaptureList(Reader.getContext(), Lambda2.Captures.front());
+ }
}
if (D->getODRHash() != MergeDD.ODRHash) {
AddDeclRef(D->getLambdaContextDecl());
AddTypeSourceInfo(Lambda.MethodTyInfo);
for (unsigned I = 0, N = Lambda.NumCaptures; I != N; ++I) {
- const LambdaCapture &Capture = Lambda.Captures[I];
+ const LambdaCapture &Capture = Lambda.Captures.front()[I];
AddSourceLocation(Capture.getLocation());
Record->push_back(Capture.isImplicit());
Record->push_back(Capture.getCaptureKind());
--- /dev/null
+// RUN: %clang_cc1 -fmodules -std=c++17 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+
+#pragma clang module build A
+module A {}
+#pragma clang module contents
+#pragma clang module begin A
+template<typename T> T f(T v) {
+ v();
+ return v;
+}
+inline auto g() {
+ int n = 0;
+ return f([=] { return n; });
+}
+
+template<typename T> constexpr T f2(T v) {
+ v();
+ return v;
+}
+constexpr auto g2() {
+ int n = 0;
+ return f2([=] { return n; });
+}
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module build B
+module B {}
+#pragma clang module contents
+#pragma clang module begin B
+template<typename T> T f(T v) {
+ v();
+ return v;
+}
+inline auto g() {
+ int n = 0;
+ return f([=] { return n; });
+}
+
+template<typename T> constexpr T f2(T v) {
+ v();
+ return v;
+}
+constexpr auto g2() {
+ int n = 0;
+ return f2([=] { return n; });
+}
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module import A
+#pragma clang module import B
+
+// CHECK: define {{.*}}use_g
+int use_g() {
+ return g()();
+}
+
+static_assert(g2()() == 0);